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
+414
View File
@@ -0,0 +1,414 @@
var $ = require('jquery');
var Backbone = require('backbone');
var Layers = require('../../../src/geo/map/layers');
var CartoDBLayer = require('../../../src/geo/map/cartodb-layer');
var TileLayer = require('../../../src/geo/map/tile-layer');
var TorqueLayer = require('../../../src/geo/map/torque-layer');
var GMapsBaseLayer = require('../../../src/geo/map/gmaps-base-layer');
var CartoDBLayerGroup = require('../../../src/geo/cartodb-layer-group');
var createEngine = require('../fixtures/engine.fixture.js');
describe('geo/cartodb-layer-group', function () {
var engineMock;
beforeEach(function () {
this.layersCollection = new Layers();
engineMock = createEngine();
this.cartoDBLayerGroup = new CartoDBLayerGroup({}, {
layersCollection: this.layersCollection
});
});
describe('.fetchAttributes', function () {
beforeEach(function () {
this.cartoDBLayerGroup.set('urls', {
attributes: [
'http://carto.com/1/attributes',
'http://carto.com/2/attributes'
]
});
spyOn($, 'ajax').and.callFake(function (options) {
options.success('attributes!');
});
});
it('should trigger a request to the right URL', function () {
var callback = jasmine.createSpy('callback');
this.cartoDBLayerGroup.fetchAttributes(0, 1000, callback);
expect(callback).toHaveBeenCalledWith('attributes!');
expect($.ajax.calls.mostRecent().args[0].url).toEqual('http://carto.com/1/attributes/1000');
});
it('should invoke the callback with null when the ajax request fails', function () {
var callback = jasmine.createSpy('callback');
$.ajax.and.callFake(function (options) {
options.error('error!');
});
this.cartoDBLayerGroup.fetchAttributes(0, 1000, callback);
expect(callback).toHaveBeenCalledWith(null);
});
it('should append the api_key to urls', function () {
this.cartoDBLayerGroup.set('apiKey', 'THE_API_KEY');
var callback = jasmine.createSpy('callback');
this.cartoDBLayerGroup.fetchAttributes(1, 1000, callback);
expect(callback).toHaveBeenCalledWith('attributes!');
expect($.ajax.calls.mostRecent().args[0].url).toEqual('http://carto.com/2/attributes/1000?api_key=THE_API_KEY');
});
it('should append the auth_token to urls', function () {
this.cartoDBLayerGroup.set('authToken', 'AUTH_TOKEN');
var callback = jasmine.createSpy('callback');
this.cartoDBLayerGroup.fetchAttributes(1, 1000, callback);
expect(callback).toHaveBeenCalledWith('attributes!');
expect($.ajax.calls.mostRecent().args[0].url).toEqual('http://carto.com/2/attributes/1000?auth_token=AUTH_TOKEN');
});
});
describe('.getTileURLTemplate', function () {
beforeEach(function () {
this.cartoDBLayerGroup = new CartoDBLayerGroup({
indexOfLayersInWindshaft: [1, 2]
}, {
layersCollection: this.layersCollection
});
var otherLayer = new Backbone.Model();
this.cartoDBLayer1 = new CartoDBLayer({}, { engine: engineMock });
this.cartoDBLayer2 = new CartoDBLayer({}, { engine: engineMock });
this.layersCollection.reset([
otherLayer,
this.cartoDBLayer1,
this.cartoDBLayer2
]);
});
it('should return an empty array there are NO urls yet', function () {
expect(this.cartoDBLayerGroup.getTileURLTemplate()).toEqual('');
});
it('should return an empty array if there are NO tile URL templates', function () {
this.cartoDBLayerGroup.set('urls', {
tiles: ''
});
expect(this.cartoDBLayerGroup.getTileURLTemplate()).toEqual('');
});
describe('png', function () {
beforeEach(function () {
this.cartoDBLayerGroup.set('urls', {
tiles: 'http://carto.com/{layerIndexes}/{z}/{x}/{y}.{format}'
});
});
it('should return an array with the tile URL templates', function () {
expect(this.cartoDBLayerGroup.getTileURLTemplate()).toEqual('http://carto.com/1,2/{z}/{x}/{y}.png');
});
it('should not include index of layers that are hidden', function () {
this.cartoDBLayer1.set('visible', false);
expect(this.cartoDBLayerGroup.getTileURLTemplate()).toEqual('http://carto.com/2/{z}/{x}/{y}.png');
});
it('should return an empty array if all layers are hidden', function () {
this.cartoDBLayer1.set('visible', false);
this.cartoDBLayer2.set('visible', false);
expect(this.cartoDBLayerGroup.getTileURLTemplate()).toEqual('');
});
it('should return an empty string if there are no layer indexes', function () {
spyOn(this.cartoDBLayerGroup, '_getIndexesOfVisibleMapnikLayers').and.returnValue('');
expect(this.cartoDBLayerGroup.getTileURLTemplate()).toEqual('');
});
it('should append the api_key to urls', function () {
this.cartoDBLayerGroup.set({
apiKey: 'THE_API_KEY'
});
expect(this.cartoDBLayerGroup.getTileURLTemplate()).toEqual('http://carto.com/1,2/{z}/{x}/{y}.png?api_key=THE_API_KEY');
});
it('should append the auth_token to urls', function () {
this.cartoDBLayerGroup.set({
authToken: 'AUTH_TOKEN'
});
expect(this.cartoDBLayerGroup.getTileURLTemplate()).toEqual('http://carto.com/1,2/{z}/{x}/{y}.png?auth_token=AUTH_TOKEN');
});
});
describe('mvt', function () {
beforeEach(function () {
this.cartoDBLayerGroup.set('urls', {
tiles: 'http://carto.com/{layerIndexes}/{z}/{x}/{y}.{format}'
});
});
it('should return a single tile URL template', function () {
expect(this.cartoDBLayerGroup.getTileURLTemplate('mvt')).toEqual('http://carto.com/mapnik/{z}/{x}/{y}.mvt');
});
it('should return a single tile URL template if all layers are hidden', function () {
this.cartoDBLayer1.set('visible', false);
this.cartoDBLayer2.set('visible', false);
expect(this.cartoDBLayerGroup.getTileURLTemplate('mvt')).toEqual('http://carto.com/mapnik/{z}/{x}/{y}.mvt');
});
it('should append the api_key to urls', function () {
this.cartoDBLayerGroup.set({
apiKey: 'THE_API_KEY'
});
expect(this.cartoDBLayerGroup.getTileURLTemplate('mvt')).toEqual('http://carto.com/mapnik/{z}/{x}/{y}.mvt?api_key=THE_API_KEY');
});
it('should append the auth_token to urls', function () {
this.cartoDBLayerGroup.set({
authToken: 'AUTH_TOKEN'
});
expect(this.cartoDBLayerGroup.getTileURLTemplate('mvt')).toEqual('http://carto.com/mapnik/{z}/{x}/{y}.mvt?auth_token=AUTH_TOKEN');
});
});
});
describe('.getTileURLTemplatesWithSubdomains', function () {
beforeEach(function () {
this.cartoDBLayerGroup = new CartoDBLayerGroup({
indexOfLayersInWindshaft: [1, 2],
urls: {
tiles: 'http://carto.com/{layerIndexes}/{z}/{x}/{y}.{format}'
}
}, {
layersCollection: this.layersCollection
});
var otherLayer = new Backbone.Model();
this.cartoDBLayer1 = new CartoDBLayer({}, { engine: engineMock });
this.cartoDBLayer2 = new CartoDBLayer({}, { engine: engineMock });
this.layersCollection.reset([
otherLayer,
this.cartoDBLayer1,
this.cartoDBLayer2
]);
});
it('should return one URL when there are NO subdomains', function () {
expect(this.cartoDBLayerGroup.getTileURLTemplatesWithSubdomains()).toEqual([ 'http://carto.com/1,2/{z}/{x}/{y}.png' ]);
});
it('should include URLs for different subdomains', function () {
this.cartoDBLayerGroup.set('urls', {
tiles: 'http://{s}.carto.com/{layerIndexes}/{z}/{x}/{y}.{format}',
subdomains: [ '0', '1', '2', '3' ]
});
expect(this.cartoDBLayerGroup.getTileURLTemplatesWithSubdomains()).toEqual([
'http://0.carto.com/1,2/{z}/{x}/{y}.png',
'http://1.carto.com/1,2/{z}/{x}/{y}.png',
'http://2.carto.com/1,2/{z}/{x}/{y}.png',
'http://3.carto.com/1,2/{z}/{x}/{y}.png'
]);
});
});
describe('.hasTileURLTemplates', function () {
beforeEach(function () {
this.cartoDBLayer1 = new CartoDBLayer({}, { engine: engineMock });
this.cartoDBLayer2 = new CartoDBLayer({}, { engine: engineMock });
this.layersCollection.reset([
this.cartoDBLayer1,
this.cartoDBLayer2
]);
this.cartoDBLayerGroup = new CartoDBLayerGroup({
indexOfLayersInWindshaft: [1, 2]
}, {
layersCollection: this.layersCollection
});
});
it('should return false if there are NO urls yet', function () {
expect(this.cartoDBLayerGroup.hasTileURLTemplates()).toBe(false);
});
it('should return false if there are NO tile URL templates', function () {
this.cartoDBLayerGroup.set('urls', {
tiles: ''
});
expect(this.cartoDBLayerGroup.hasTileURLTemplates()).toBe(false);
});
it('should return true if there are tile URL templates', function () {
this.cartoDBLayerGroup.set('urls', {
tiles: 'url1'
});
expect(this.cartoDBLayerGroup.hasTileURLTemplates()).toBe(true);
});
});
describe('.getGridURLTemplatesWithSubdomains', function () {
beforeEach(function () {
this.cartoDBLayerGroup = new CartoDBLayerGroup({}, {
layersCollection: this.layersCollection
});
});
it('should return an empty array there are NO urls yet', function () {
expect(this.cartoDBLayerGroup.getGridURLTemplatesWithSubdomains(0)).toEqual([]);
expect(this.cartoDBLayerGroup.getGridURLTemplatesWithSubdomains(1)).toEqual([]);
});
describe("when there're grid URLs", function () {
beforeEach(function () {
this.cartoDBLayerGroup.set('urls', {
grids: [
[ 'url1' ],
[ 'url2' ]
]
});
});
it('should return an array with the grid URL templates', function () {
expect(this.cartoDBLayerGroup.getGridURLTemplatesWithSubdomains(0)).toEqual([ 'url1' ]);
expect(this.cartoDBLayerGroup.getGridURLTemplatesWithSubdomains(1)).toEqual([ 'url2' ]);
});
it('should append the api_key to urls', function () {
this.cartoDBLayerGroup.set({
apiKey: 'THE_API_KEY'
});
expect(this.cartoDBLayerGroup.getGridURLTemplatesWithSubdomains(0)).toEqual([ 'url1?api_key=THE_API_KEY' ]);
expect(this.cartoDBLayerGroup.getGridURLTemplatesWithSubdomains(1)).toEqual([ 'url2?api_key=THE_API_KEY' ]);
});
it('should append the auth_token to urls', function () {
this.cartoDBLayerGroup.set({
authToken: 'AUTH_TOKEN'
});
expect(this.cartoDBLayerGroup.getGridURLTemplatesWithSubdomains(0)).toEqual([ 'url1?auth_token=AUTH_TOKEN' ]);
expect(this.cartoDBLayerGroup.getGridURLTemplatesWithSubdomains(1)).toEqual([ 'url2?auth_token=AUTH_TOKEN' ]);
});
});
});
describe('.getAttributesBaseURL', function () {
});
describe('.getStaticImageURLTemplate', function () {
beforeEach(function () {
this.baseLayer = new TileLayer({}, { engine: engineMock });
this.cartoDBLayer1 = new CartoDBLayer({}, { engine: engineMock });
this.cartoDBLayer2 = new CartoDBLayer({}, { engine: engineMock });
this.torqueLayer = new TorqueLayer({}, { engine: engineMock });
this.labelsLayer = new TileLayer({}, { engine: engineMock });
this.layersCollection.reset([
this.baseLayer,
this.cartoDBLayer1,
this.cartoDBLayer2,
this.torqueLayer,
this.labelsLayer
]);
this.cartoDBLayerGroup.set('urls', {
image: 'http://carto.com/image'
});
});
it('should include indexes of visible layers', function () {
expect(this.cartoDBLayerGroup.getStaticImageURLTemplate()).toEqual('http://carto.com/image?layer=0,1,2,3,4');
});
it('should not include hidden layers', function () {
this.cartoDBLayer1.hide();
this.torqueLayer.hide();
expect(this.cartoDBLayerGroup.getStaticImageURLTemplate()).toEqual('http://carto.com/image?layer=0,2,4');
});
it('should ignore Google Maps base layers (Maps API is not aware of them)', function () {
this.baseLayer = new GMapsBaseLayer(null);
this.cartoDBLayer1 = new CartoDBLayer({}, { engine: engineMock });
this.cartoDBLayer2 = new CartoDBLayer({}, { engine: engineMock });
this.layersCollection.reset([
this.baseLayer,
this.cartoDBLayer1,
this.cartoDBLayer2
]);
expect(this.cartoDBLayerGroup.getStaticImageURLTemplate()).toEqual('http://carto.com/image?layer=0,1');
});
it('should include api_key param', function () {
this.cartoDBLayerGroup.set({
apiKey: 'THE_API_KEY'
});
expect(this.cartoDBLayerGroup.getStaticImageURLTemplate()).toEqual('http://carto.com/image?layer=0,1,2,3,4&api_key=THE_API_KEY');
});
it('should include auth_token param', function () {
this.cartoDBLayerGroup.set({
authToken: 'AUTH_TOKEN'
});
expect(this.cartoDBLayerGroup.getStaticImageURLTemplate()).toEqual('http://carto.com/image?layer=0,1,2,3,4&auth_token=AUTH_TOKEN');
});
it('should include subdomains', function () {
this.cartoDBLayerGroup.set('urls', {
image: 'http://{s}.carto.com/image',
subdomains: [ '0', '1' ]
});
expect(this.cartoDBLayerGroup.getStaticImageURLTemplate()).toEqual('http://0.carto.com/image?layer=0,1,2,3,4');
});
});
describe('.addError', function () {
it('should throw an error if the error does not have a type', function () {
expect(this.cartoDBLayerGroup.addError).toThrow();
});
it('should trigger an error with the specified type', function () {
var called = '';
this.cartoDBLayerGroup.on('error:limit', function () {
called = 'limit';
});
this.cartoDBLayerGroup.on('error:tile', function () {
called = 'tile';
});
this.cartoDBLayerGroup.addError({ type: 'limit' });
expect(called).toEqual('limit');
this.cartoDBLayerGroup.addError({ type: 'tile' });
expect(called).toEqual('tile');
});
});
});
@@ -0,0 +1,261 @@
{
"type": "FeatureCollection",
"query": [
"los",
"angeles"
],
"features": [
{
"id": "place.9962989141465270",
"type": "Feature",
"place_type": [
"place"
],
"relevance": 0.99,
"properties": {
"wikidata": "Q65"
},
"text": "Los Angeles",
"place_name": "Los Angeles, California, United States",
"bbox": [
-118.529221009603,
33.901599990108,
-118.121099990025,
34.1612200099034
],
"center": [
-118.2439,
34.0544
],
"geometry": {
"type": "Point",
"coordinates": [
-118.2439,
34.0544
]
},
"context": [
{
"id": "region.3591",
"short_code": "US-CA",
"wikidata": "Q99",
"text": "California"
},
{
"id": "country.3145",
"short_code": "us",
"wikidata": "Q30",
"text": "United States"
}
]
},
{
"id": "place.10952642230180310",
"type": "Feature",
"place_type": [
"place"
],
"relevance": 0.99,
"properties": {
"wikidata": "Q16910"
},
"text": "Los Ángeles",
"place_name": "Los Ángeles, Bío Bío, Chile",
"bbox": [
-72.68248,
-37.663862,
-72.041277,
-37.178368
],
"center": [
-72.35,
-37.46667
],
"geometry": {
"type": "Point",
"coordinates": [
-72.35,
-37.46667
]
},
"context": [
{
"id": "region.3552",
"short_code": "CL-BI",
"wikidata": "Q2170",
"text": "Bío Bío"
},
{
"id": "country.344",
"short_code": "cl",
"wikidata": "Q298",
"text": "Chile"
}
]
},
{
"id": "poi.1611278850983920",
"type": "Feature",
"place_type": [
"poi"
],
"relevance": 0.99,
"properties": {
"address": "1 World Way",
"category": "international airport, airport",
"tel": "(310) 646-5252",
"wikidata": "Q8731",
"landmark": true,
"maki": "airport"
},
"text": "Los Angeles International Airport",
"place_name": "Los Angeles International Airport, 1 World Way, Los Angeles, California 90045, United States",
"center": [
-118.408056,
33.9425
],
"geometry": {
"coordinates": [
-118.408056,
33.9425
],
"type": "Point"
},
"context": [
{
"id": "neighborhood.33720",
"text": "Westchester"
},
{
"id": "postcode.8081932850252730",
"text": "90045"
},
{
"id": "place.9962989141465270",
"wikidata": "Q65",
"text": "Los Angeles"
},
{
"id": "region.3591",
"short_code": "US-CA",
"wikidata": "Q99",
"text": "California"
},
{
"id": "country.3145",
"short_code": "us",
"wikidata": "Q30",
"text": "United States"
}
]
},
{
"id": "neighborhood.2104633",
"type": "Feature",
"place_type": [
"neighborhood"
],
"relevance": 0.99,
"properties": {},
"text": "Los Angeles Heights - Keystone",
"place_name": "Los Angeles Heights - Keystone, San Antonio, Texas 78201, United States",
"bbox": [
-98.534942,
29.453364,
-98.514652,
29.485214
],
"center": [
-98.52,
29.47
],
"geometry": {
"type": "Point",
"coordinates": [
-98.52,
29.47
]
},
"context": [
{
"id": "postcode.7069290925572850",
"text": "78201"
},
{
"id": "place.7705127234253710",
"wikidata": "Q975",
"text": "San Antonio"
},
{
"id": "region.3818",
"short_code": "US-TX",
"wikidata": "Q1439",
"text": "Texas"
},
{
"id": "country.3145",
"short_code": "us",
"wikidata": "Q30",
"text": "United States"
}
]
},
{
"id": "poi.17555628334440500",
"type": "Feature",
"place_type": [
"poi"
],
"relevance": 0.99,
"properties": {
"address": "3939 S Figueroa St",
"category": "stadium, arena",
"tel": "(213) 747-7111",
"wikidata": "Q849784",
"landmark": true,
"maki": "baseball"
},
"text": "Los Angeles Memorial Coliseum",
"place_name": "Los Angeles Memorial Coliseum, 3939 S Figueroa St, Los Angeles, California 90037, United States",
"center": [
-118.287778,
34.014167
],
"geometry": {
"coordinates": [
-118.287778,
34.014167
],
"type": "Point"
},
"context": [
{
"id": "neighborhood.293901",
"text": "South Los Angeles"
},
{
"id": "postcode.15612390715732530",
"text": "90037"
},
{
"id": "place.9962989141465270",
"wikidata": "Q65",
"text": "Los Angeles"
},
{
"id": "region.3591",
"short_code": "US-CA",
"wikidata": "Q99",
"text": "California"
},
{
"id": "country.3145",
"short_code": "us",
"wikidata": "Q30",
"text": "United States"
}
]
}
],
"attribution": "NOTICE: © 2017 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained."
}
@@ -0,0 +1,268 @@
{
"type": "FeatureCollection",
"query": [
"plaza",
"de",
"barcelos"
],
"features": [
{
"id": "poi.10895640694945210",
"type": "Feature",
"place_type": [
"poi"
],
"relevance": 0.6666666666666666,
"properties": {
"landmark": true,
"tel": "07524 914990",
"category": "cafe, coffee, restaurant, tea, tea house",
"address": "Ravensburger Straße 8"
},
"text": "Plaza",
"place_name": "Plaza, Ravensburger Straße 8, Bad Waldsee, Baden-Württemberg 88339, Germany",
"center": [
9.754478,
47.920347
],
"geometry": {
"type": "Point",
"coordinates": [
9.754478,
47.920347
]
},
"context": [
{
"id": "postcode.11130053318616830",
"text": "88339"
},
{
"id": "place.11130053318505290",
"wikidata": "Q515423",
"text": "Bad Waldsee"
},
{
"id": "region.3495",
"short_code": "DE-BW",
"wikidata": "Q985",
"text": "Baden-Württemberg"
},
{
"id": "country.3135",
"short_code": "de",
"wikidata": "Q183",
"text": "Germany"
}
]
},
{
"id": "poi.13433334923344580",
"type": "Feature",
"place_type": [
"poi"
],
"relevance": 0.6666666666666666,
"properties": {
"landmark": true,
"tel": "06403 74200",
"category": "rail station, train station",
"address": "Alte Heerstraße 27",
"maki": "rail"
},
"text": "Platz",
"place_name": "Platz, Alte Heerstraße 27, Bodolz, Bayern 88131, Germany",
"center": [
9.667166,
47.567462
],
"geometry": {
"type": "Point",
"coordinates": [
9.667166,
47.567462
]
},
"context": [
{
"id": "locality.13257312967514310",
"wikidata": "Q11696975",
"text": "Enzisweiler"
},
{
"id": "postcode.7704245689115710",
"text": "88131"
},
{
"id": "place.11779142489218390",
"wikidata": "Q552427",
"text": "Bodolz"
},
{
"id": "region.3186",
"short_code": "DE-BY",
"wikidata": "Q980",
"text": "Bayern"
},
{
"id": "country.3135",
"short_code": "de",
"wikidata": "Q183",
"text": "Germany"
}
]
},
{
"id": "poi.11479188654945210",
"type": "Feature",
"place_type": [
"poi"
],
"relevance": 0.6666666666666666,
"properties": {
"landmark": true,
"tel": "0921 5160544",
"category": "restaurant",
"address": "Kirchgasse 24",
"maki": "restaurant"
},
"text": "Plaza",
"place_name": "Plaza, Kirchgasse 24, Bayreuth, Bayern 95444, Germany",
"center": [
11.57395,
49.94327
],
"geometry": {
"type": "Point",
"coordinates": [
11.57395,
49.94327
]
},
"context": [
{
"id": "postcode.14977288459298330",
"text": "95444"
},
{
"id": "place.14796666081196300",
"wikidata": "Q3923",
"text": "Bayreuth"
},
{
"id": "region.3186",
"short_code": "DE-BY",
"wikidata": "Q980",
"text": "Bayern"
},
{
"id": "country.3135",
"short_code": "de",
"wikidata": "Q183",
"text": "Germany"
}
]
},
{
"id": "poi.9477875054945210",
"type": "Feature",
"place_type": [
"poi"
],
"relevance": 0.3333333333333333,
"properties": {
"landmark": true,
"tel": "0226 344 306",
"category": "restaurant",
"maki": "restaurant"
},
"text": "Plaza",
"place_name": "Plaza, Beringe, Limburg 5986, Netherlands",
"center": [
5.947389,
51.337527
],
"geometry": {
"type": "Point",
"coordinates": [
5.947389,
51.337527
]
},
"context": [
{
"id": "postcode.6633279068411020",
"text": "5986"
},
{
"id": "place.6017505065203911",
"wikidata": "Q1878867",
"text": "Beringe"
},
{
"id": "region.213188",
"short_code": "NL-LI",
"wikidata": "Q1093",
"text": "Limburg"
},
{
"id": "country.3101",
"short_code": "nl",
"wikidata": "Q55",
"text": "Netherlands"
}
]
},
{
"id": "poi.6170918672945210",
"type": "Feature",
"place_type": [
"poi"
],
"relevance": 0.3333333333333333,
"properties": {
"landmark": true,
"tel": "0382 559413",
"category": "hotel, lodging, motel",
"address": "Via Togliatti 39"
},
"text": "Plaza",
"place_name": "Plaza, Via Togliatti 39, San Martino Siccomario, Pavia 27028, Italy",
"center": [
9.130854,
45.159336
],
"geometry": {
"type": "Point",
"coordinates": [
9.130854,
45.159336
]
},
"context": [
{
"id": "postcode.12821918527764180",
"text": "27028"
},
{
"id": "place.12821918528623280",
"wikidata": "Q41026",
"text": "San Martino Siccomario"
},
{
"id": "region.3790",
"short_code": "IT-PV",
"wikidata": "Q16231",
"text": "Pavia"
},
{
"id": "country.333",
"short_code": "it",
"wikidata": "Q38",
"text": "Italy"
}
]
}
],
"attribution": "NOTICE: © 2017 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained."
}
@@ -0,0 +1,67 @@
var mapboxGeocoder = require('../../../../src/geo/geocoder/mapbox-geocoder');
var TOKEN = 'fake_token';
describe('mapbox-geocoder', function () {
describe('.geocode', function () {
it('should build the right fetch url (add address and access_token)', function (done) {
spyOn(window, 'fetch').and.returnValue(Promise.resolve({ json: function () { return require('./mapbox-geocoder-response-0'); } }));
mapboxGeocoder.geocode('fake_address', TOKEN).then(function (result) {
var expectedFetchUrl = 'https://api.mapbox.com/geocoding/v5/mapbox.places-permanent/fake_address.json?access_token=fake_token';
expect(window.fetch).toHaveBeenCalledWith(expectedFetchUrl);
done();
});
});
it('should geocode a city location', function (done) {
spyOn(window, 'fetch').and.returnValue(Promise.resolve({ json: function () { return require('./mapbox-geocoder-response-0'); } }));
mapboxGeocoder.geocode('Vigo', TOKEN)
.then(function (results) {
expect(results).toBeDefined();
done();
});
});
it('should return a well formated response [example 0]', function (done) {
spyOn(window, 'fetch').and.returnValue(Promise.resolve({ json: function () { return require('./mapbox-geocoder-response-0'); } }));
mapboxGeocoder.geocode('Vigo', TOKEN)
.then(function (result) {
result = result[0];
expect(result.center).toBeDefined();
expect(result.center[0]).toEqual(34.0544);
expect(result.center[1]).toEqual(-118.2439);
// Bbox
expect(result.boundingbox.south).toEqual(-118.529221009603);
expect(result.boundingbox.west).toEqual(33.901599990108);
expect(result.boundingbox.north).toEqual(-118.121099990025);
expect(result.boundingbox.east).toEqual(34.1612200099034);
// Type
expect(result.type).toEqual('venue');
done();
}).catch(console.error);
});
it('should return a well formated response when the response has no bbox [example 1]', function (done) {
spyOn(window, 'fetch').and.returnValue(Promise.resolve({ json: function () { return require('./mapbox-geocoder-response-1'); } }));
mapboxGeocoder.geocode('Plaza de Barcelos', TOKEN)
.then(function (result) {
result = result[0];
expect(result.center).toBeDefined();
expect(result.center[0]).toEqual(47.920347);
expect(result.center[1]).toEqual(9.754478);
// Bbox
expect(result.bbox).toBeUndefined();
// Type
expect(result.type).toEqual('venue');
done();
}).catch(console.error);
});
it('should return an empty array when the response is empty', function (done) {
spyOn(window, 'fetch').and.returnValue(Promise.resolve({ json: function () { return { features: [] }; } }));
mapboxGeocoder.geocode('Vigo', TOKEN)
.then(function (result) {
expect(result).toEqual([]);
done();
});
});
});
});
@@ -0,0 +1,445 @@
{
"summary": {
"query": "santander",
"queryType": "NON_NEAR",
"queryTime": 39,
"numResults": 10,
"offset": 0,
"totalResults": 24811,
"fuzzyLevel": 1
},
"results": [
{
"type": "Geography",
"id": "ES/GEO/p0/7661",
"score": 2.418,
"entityType": "Municipality",
"address": {
"municipality": "Santander",
"countrySecondarySubdivision": "Cantabria",
"countrySubdivision": "Cantabria",
"countryCode": "ES",
"country": "Spain",
"countryCodeISO3": "ESP",
"freeformAddress": "Santander"
},
"position": {
"lat": 43.46141,
"lon": -3.8093
},
"viewport": {
"topLeftPoint": {
"lat": 43.49482,
"lon": -3.88905
},
"btmRightPoint": {
"lat": 43.43446,
"lon": -3.76325
}
},
"boundingBox": {
"topLeftPoint": {
"lat": 43.49482,
"lon": -3.88905
},
"btmRightPoint": {
"lat": 43.43446,
"lon": -3.76325
}
},
"dataSources": {
"geometry": {
"id": "00005858-5800-1200-0000-00007d2e35e3"
}
}
},
{
"type": "Geography",
"id": "PH/GEO/p0/1587",
"score": 2.382,
"entityType": "Municipality",
"address": {
"municipality": "Santander",
"countrySecondarySubdivision": "Cebu",
"countrySubdivision": "Central Visayas",
"countryCode": "PH",
"country": "Philippines",
"countryCodeISO3": "PHL",
"freeformAddress": "Santander, Central Visayas"
},
"position": {
"lat": 9.42242,
"lon": 123.30378
},
"viewport": {
"topLeftPoint": {
"lat": 9.47779,
"lon": 123.29687
},
"btmRightPoint": {
"lat": 9.41298,
"lon": 123.3632
}
},
"boundingBox": {
"topLeftPoint": {
"lat": 9.47779,
"lon": 123.29687
},
"btmRightPoint": {
"lat": 9.41298,
"lon": 123.3632
}
},
"dataSources": {
"geometry": {
"id": "00005858-5800-1200-0000-000077378fa7"
}
}
},
{
"type": "Geography",
"id": "MX/GEO/p0/340",
"score": 2.362,
"entityType": "Municipality",
"address": {
"municipality": "Jiménez",
"countrySubdivision": "Tamaulipas",
"countryCode": "MX",
"country": "Mexico",
"countryCodeISO3": "MEX",
"freeformAddress": "Jiménez"
},
"position": {
"lat": 24.21378,
"lon": -98.48187
},
"viewport": {
"topLeftPoint": {
"lat": 24.47171,
"lon": -98.72639
},
"btmRightPoint": {
"lat": 23.93939,
"lon": -98.27799
}
},
"boundingBox": {
"topLeftPoint": {
"lat": 24.47171,
"lon": -98.72639
},
"btmRightPoint": {
"lat": 23.93939,
"lon": -98.27799
}
},
"dataSources": {
"geometry": {
"id": "00005858-5800-1200-0000-00007737d289"
}
}
},
{
"type": "Geography",
"id": "CO/GEO/p0/252",
"score": 2.36,
"entityType": "Municipality",
"address": {
"municipality": "Puerto Santander",
"countrySubdivision": "Norte de Santander",
"countryCode": "CO",
"country": "Colombia",
"countryCodeISO3": "COL",
"freeformAddress": "Puerto Santander, Norte de Santander"
},
"position": {
"lat": 8.36227,
"lon": -72.40896
},
"viewport": {
"topLeftPoint": {
"lat": 8.38354,
"lon": -72.4475
},
"btmRightPoint": {
"lat": 8.26839,
"lon": -72.38042
}
},
"boundingBox": {
"topLeftPoint": {
"lat": 8.38354,
"lon": -72.4475
},
"btmRightPoint": {
"lat": 8.26839,
"lon": -72.38042
}
},
"dataSources": {
"geometry": {
"id": "00005858-5800-1200-0000-00007d30e01c"
}
}
},
{
"type": "Geography",
"id": "CO/GEO/p0/888",
"score": 2.358,
"entityType": "Municipality",
"address": {
"municipality": "Santander",
"countrySubdivision": "Amazonas",
"countryCode": "CO",
"country": "Colombia",
"countryCodeISO3": "COL",
"freeformAddress": "Santander, Amazonas"
},
"position": {
"lat": -0.62217,
"lon": -72.38281
},
"viewport": {
"topLeftPoint": {
"lat": -0.26464,
"lon": -72.99754
},
"btmRightPoint": {
"lat": -1.89121,
"lon": -70.72647
}
},
"boundingBox": {
"topLeftPoint": {
"lat": -0.26464,
"lon": -72.99754
},
"btmRightPoint": {
"lat": -1.89121,
"lon": -70.72647
}
},
"dataSources": {
"geometry": {
"id": "00005858-5800-1200-0000-00007d30ef7b"
}
}
},
{
"type": "Geography",
"id": "CO/GEO/p0/1010",
"score": 2.341,
"entityType": "Municipality",
"address": {
"municipality": "Santander de Quilichao",
"countrySubdivision": "Cauca",
"countryCode": "CO",
"country": "Colombia",
"countryCodeISO3": "COL",
"freeformAddress": "Santander de Quilichao, Cauca"
},
"position": {
"lat": 3.00879,
"lon": -76.4859
},
"viewport": {
"topLeftPoint": {
"lat": 3.1493,
"lon": -76.61535
},
"btmRightPoint": {
"lat": 2.8461,
"lon": -76.37358
}
},
"boundingBox": {
"topLeftPoint": {
"lat": 3.1493,
"lon": -76.61535
},
"btmRightPoint": {
"lat": 2.8461,
"lon": -76.37358
}
},
"dataSources": {
"geometry": {
"id": "00005858-5800-1200-0000-00007d30eff7"
}
}
},
{
"type": "Geography",
"id": "CO/GEO/p0/6",
"score": 2.255,
"entityType": "CountrySubdivision",
"address": {
"countrySubdivision": "Santander",
"countryCode": "CO",
"country": "Colombia",
"countryCodeISO3": "COL",
"freeformAddress": "Santander"
},
"position": {
"lat": 6.92507,
"lon": -73.50153
},
"viewport": {
"topLeftPoint": {
"lat": 8.14342,
"lon": -74.52666
},
"btmRightPoint": {
"lat": 5.70671,
"lon": -72.4764
}
},
"boundingBox": {
"topLeftPoint": {
"lat": 8.14342,
"lon": -74.52666
},
"btmRightPoint": {
"lat": 5.70671,
"lon": -72.4764
}
},
"dataSources": {
"geometry": {
"id": "00005858-5800-1200-0000-000077374466"
}
}
},
{
"type": "Geography",
"id": "CO/GEO/p0/5",
"score": 2.211,
"entityType": "CountrySubdivision",
"address": {
"countrySubdivision": "Norte de Santander",
"countryCode": "CO",
"country": "Colombia",
"countryCodeISO3": "COL",
"freeformAddress": "Norte de Santander"
},
"position": {
"lat": 8.08458,
"lon": -72.84278
},
"viewport": {
"topLeftPoint": {
"lat": 9.2961,
"lon": -73.63743
},
"btmRightPoint": {
"lat": 6.87306,
"lon": -72.04813
}
},
"boundingBox": {
"topLeftPoint": {
"lat": 9.2961,
"lon": -73.63743
},
"btmRightPoint": {
"lat": 6.87306,
"lon": -72.04813
}
},
"dataSources": {
"geometry": {
"id": "00005858-5800-1200-0000-000077374465"
}
}
},
{
"type": "Geography",
"id": "CO/GEO/p0/1668",
"score": 2.177,
"entityType": "MunicipalitySubdivision",
"address": {
"municipalitySubdivision": "Santander",
"municipality": "Cali",
"countrySubdivision": "Valle del Cauca",
"countryCode": "CO",
"country": "Colombia",
"countryCodeISO3": "COL",
"freeformAddress": "Cali Santander, Valle del Cauca"
},
"position": {
"lat": 3.46266,
"lon": -76.51554
},
"viewport": {
"topLeftPoint": {
"lat": 3.4665,
"lon": -76.52026
},
"btmRightPoint": {
"lat": 3.45905,
"lon": -76.51054
}
},
"boundingBox": {
"topLeftPoint": {
"lat": 3.4665,
"lon": -76.52026
},
"btmRightPoint": {
"lat": 3.45905,
"lon": -76.51054
}
},
"dataSources": {
"geometry": {
"id": "0000434f-3300-3c00-0000-000065ec89e3"
}
}
},
{
"type": "Geography",
"id": "CO/GEO/p0/2477",
"score": 2.177,
"entityType": "MunicipalitySubdivision",
"address": {
"municipalitySubdivision": "Santander",
"municipality": "Medellín",
"countrySubdivision": "Antioquia",
"countryCode": "CO",
"country": "Colombia",
"countryCodeISO3": "COL",
"freeformAddress": "Medellín Santander, Antioquia"
},
"position": {
"lat": 6.30768,
"lon": -75.57501
},
"viewport": {
"topLeftPoint": {
"lat": 6.31099,
"lon": -75.57874
},
"btmRightPoint": {
"lat": 6.30185,
"lon": -75.57128
}
},
"boundingBox": {
"topLeftPoint": {
"lat": 6.31099,
"lon": -75.57874
},
"btmRightPoint": {
"lat": 6.30185,
"lon": -75.57128
}
},
"dataSources": {
"geometry": {
"id": "0000434f-3200-3c00-0000-000065a03d92"
}
}
}
]
}
@@ -0,0 +1,51 @@
var tomtomGeocoder = require('../../../../src/geo/geocoder/tomtom-geocoder');
var API_KEY = 'fake_api_key';
describe('tomtom-geocoder', function () {
describe('.geocode', function () {
it('should build the right fetch url (add address and apiKey)', function (done) {
spyOn(window, 'fetch').and.returnValue(Promise.resolve({ json: function () { return { results: [] }; } }));
tomtomGeocoder.geocode('fake_address', API_KEY).then(function (result) {
var expectedFetchUrl = 'https://api.tomtom.com/search/2/search/fake_address.json?key=fake_api_key';
expect(window.fetch).toHaveBeenCalledWith(expectedFetchUrl);
done();
});
});
it('should geocode a city location', function (done) {
spyOn(window, 'fetch').and.returnValue(Promise.resolve({ json: function () { return require('./tomtom-geocoder-response-0'); } }));
tomtomGeocoder.geocode('Santander', API_KEY)
.then(function (results) {
expect(results).toBeDefined();
done();
});
});
it('should return a well formated response [example 0]', function (done) {
spyOn(window, 'fetch').and.returnValue(Promise.resolve({ json: function () { return require('./tomtom-geocoder-response-0'); } }));
tomtomGeocoder.geocode('Santander', API_KEY)
.then(function (results) {
let bestCandidate = results[0];
expect(bestCandidate.center).toBeDefined();
expect(bestCandidate.center[0]).toEqual(43.46141); // lat
expect(bestCandidate.center[1]).toEqual(-3.8093); // lon
// Bbox
expect(bestCandidate.boundingbox.south).toEqual(43.43446);
expect(bestCandidate.boundingbox.west).toEqual(-3.88905);
expect(bestCandidate.boundingbox.north).toEqual(43.49482);
expect(bestCandidate.boundingbox.east).toEqual(-3.76325);
// Type
expect(bestCandidate.type).toEqual('localadmin');
done();
}).catch(console.error);
});
it('should return an empty array when the response is empty', function (done) {
spyOn(window, 'fetch').and.returnValue(Promise.resolve({ json: function () { return { results: [] }; } }));
tomtomGeocoder.geocode('ABCDEFGHIJ', API_KEY)
.then(function (result) {
expect(result).toEqual([]);
done();
});
});
});
});
@@ -0,0 +1,148 @@
var _ = require('underscore');
var GeometryFactory = require('../../../../src/geo/geometry-models/geometry-factory');
var Point = require('../../../../src/geo/geometry-models/point');
var Polyline = require('../../../../src/geo/geometry-models/polyline');
var Polygon = require('../../../../src/geo/geometry-models/polygon');
var MultiPoint = require('../../../../src/geo/geometry-models/multi-point');
var MultiPolygon = require('../../../../src/geo/geometry-models/multi-polygon');
var MultiPolyline = require('../../../../src/geo/geometry-models/multi-polyline');
var geometryAsFeature = function (geometry) {
return {
'type': 'Feature',
'properties': {},
'geometry': geometry
};
};
describe('src/geo/geometry-models/geometry-factory', function () {
describe('.createGeometryFromGeoJSON', function () {
var pointGeometry = {
'type': 'Point',
'coordinates': [
-3.779296875,
40.245991504199026
]
};
_.each([ pointGeometry, geometryAsFeature(pointGeometry) ], function (geoJSON) {
it('should create a point', function () {
var geometry = GeometryFactory.createGeometryFromGeoJSON(geoJSON);
expect(geometry instanceof Point).toBeTruthy();
expect(geometry.getCoordinates()).toEqual([ 40.245991504199026, -3.779296875 ]);
});
});
var polylineGeometry = {
'type': 'LineString',
'coordinates': [
[
-2.021484375,
43.51668853502906
],
[
3.6035156249999996,
42.293564192170095
]
]
};
_.each([ polylineGeometry, geometryAsFeature(polylineGeometry) ], function (geoJSON) {
it('should create a polyline', function () {
var geometry = GeometryFactory.createGeometryFromGeoJSON(geoJSON);
expect(geometry instanceof Polyline).toBeTruthy();
expect(geometry.getCoordinates()).toEqual([
[ 43.51668853502906, -2.021484375 ],
[ 42.293564192170095, 3.6035156249999996 ]
]);
});
});
var polygonGeometry = {
'type': 'Polygon',
'coordinates': [
[
[
-8.96484375,
41.918628865183045
],
[
-7.84423828125,
43.74728909225908
],
[
-1.69189453125,
43.34914966389313
],
[
-8.96484375,
41.918628865183045
]
]
]
};
_.each([ polygonGeometry, geometryAsFeature(polygonGeometry) ], function (geoJSON) {
it('should create a polygon', function () {
var geometry = GeometryFactory.createGeometryFromGeoJSON(geoJSON);
expect(geometry instanceof Polygon).toBeTruthy();
expect(geometry.getCoordinates()).toEqual([
[ 41.918628865183045, -8.96484375 ],
[ 43.74728909225908, -7.84423828125 ],
[ 43.34914966389313, -1.69189453125 ]
]);
});
});
var multiPointGeometry = {
'type': 'MultiPoint',
'coordinates': [ [100.0, 0.0], [101.0, 1.0] ]
};
it('should create a multipoint', function () {
var geometry = GeometryFactory.createGeometryFromGeoJSON(multiPointGeometry);
expect(geometry instanceof MultiPoint).toBeTruthy();
expect(geometry.geometries.length).toEqual(2);
expect(geometry.geometries.at(0).getCoordinates()).toEqual([ 0, 100 ]);
expect(geometry.geometries.at(1).getCoordinates()).toEqual([ 1, 101 ]);
});
var multiPolygonGeometry = {
'type': 'MultiPolygon',
'coordinates': [
[ [ [102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0] ] ],
[ [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ] ]
]
};
it('should create a multipolygon', function () {
var geometry = GeometryFactory.createGeometryFromGeoJSON(multiPolygonGeometry);
expect(geometry instanceof MultiPolygon).toBeTruthy();
expect(geometry.geometries.length).toEqual(2);
expect(geometry.geometries.at(0).getCoordinates()).toEqual([ [ 2, 102 ], [ 2, 103 ], [ 3, 103 ], [ 3, 102 ] ]);
expect(geometry.geometries.at(1).getCoordinates()).toEqual([ [ 0, 100 ], [ 0, 101 ], [ 1, 101 ], [ 1, 100 ] ]);
});
var multiPolylineGeometry = {
'type': 'MultiLineString',
'coordinates': [
[ [100.0, 0.0], [101.0, 1.0] ],
[ [102.0, 2.0], [103.0, 3.0] ]
]
};
it('should create a multipolyline', function () {
var geometry = GeometryFactory.createGeometryFromGeoJSON(multiPolylineGeometry);
expect(geometry instanceof MultiPolyline).toBeTruthy();
expect(geometry.geometries.length).toEqual(2);
expect(geometry.geometries.at(0).getCoordinates()).toEqual([ [ 0, 100 ], [ 1, 101 ] ]);
expect(geometry.geometries.at(1).getCoordinates()).toEqual([ [ 2, 102 ], [ 3, 103 ] ]);
});
});
});
@@ -0,0 +1,32 @@
var MultiPoint = require('../../../../src/geo/geometry-models/multi-point');
describe('src/geo/geometry-models/multi-point', function () {
beforeEach(function () {
this.multiPoint = new MultiPoint(null, {
latlngs: [
[0, 1],
[1, 2]
]
});
});
describe('.toGeoJSON', function () {
it('should generate the GeoJSON correctly', function () {
expect(this.multiPoint.toGeoJSON()).toEqual({
type: 'MultiPoint',
coordinates: [ [ 1, 0 ], [ 2, 1 ] ]
});
});
});
describe('.setCoordinatesFromGeoJSON', function () {
it('should update the coordinates', function () {
var newAndExpectedGeoJSON = {
type: 'MultiPoint',
coordinates: [ [ 0, 0 ], [ 10, 10 ] ]
};
this.multiPoint.setCoordinatesFromGeoJSON(newAndExpectedGeoJSON);
expect(this.multiPoint.toGeoJSON()).toEqual(newAndExpectedGeoJSON);
});
});
});
@@ -0,0 +1,54 @@
var MultiPolygon = require('../../../../src/geo/geometry-models/multi-polygon');
describe('src/geo/geometry-models/multi-polygon', function () {
beforeEach(function () {
this.multiPolygon = new MultiPolygon(null, {
latlngs: [
[
[0, 1],
[1, 2],
[2, 3],
[3, 4]
],
[
[0, 10],
[10, 20],
[20, 30],
[30, 40]
]
]
});
});
describe('.toGeoJSON', function () {
it('should generate the GeoJSON correctly', function () {
expect(this.multiPolygon.toGeoJSON()).toEqual({
type: 'MultiPolygon',
coordinates: [
[
[ [ 1, 0 ], [ 2, 1 ], [ 3, 2 ], [ 4, 3 ], [ 1, 0 ] ]
], [
[ [ 10, 0 ], [ 20, 10 ], [ 30, 20 ], [ 40, 30 ], [ 10, 0 ] ]
]
]
});
});
});
describe('.setCoordinatesFromGeoJSON', function () {
it('should update the coordinates', function () {
var newAndExpectedGeoJSON = {
type: 'MultiPolygon',
coordinates: [
[
[ [ 0, 0 ], [ 2, 1 ], [ 3, 2 ], [ 4, 3 ], [ 0, 0 ] ]
], [
[ [ 100, 0 ], [ 20, 10 ], [ 30, 20 ], [ 40, 30 ], [ 100, 0 ] ]
]
]
};
this.multiPolygon.setCoordinatesFromGeoJSON(newAndExpectedGeoJSON);
expect(this.multiPolygon.toGeoJSON()).toEqual(newAndExpectedGeoJSON);
});
});
});
@@ -0,0 +1,54 @@
var MultiPolyline = require('../../../../src/geo/geometry-models/multi-polyline');
describe('src/geo/geometry-models/multi-polyline', function () {
beforeEach(function () {
this.multiPolyline = new MultiPolyline(null, {
latlngs: [
[
[0, 1],
[1, 2],
[2, 3],
[3, 4]
],
[
[0, 10],
[10, 20],
[20, 30],
[30, 40]
]
]
});
});
describe('.toGeoJSON', function () {
it('should generate the GeoJSON correctly', function () {
expect(this.multiPolyline.toGeoJSON()).toEqual({
type: 'MultiLineString',
coordinates: [
[
[ 1, 0 ], [ 2, 1 ], [ 3, 2 ], [ 4, 3 ]
], [
[ 10, 0 ], [ 20, 10 ], [ 30, 20 ], [ 40, 30 ]
]
]
});
});
});
describe('.setCoordinatesFromGeoJSON', function () {
it('should update the coordinates', function () {
var newAndExpectedGeoJSON = {
type: 'MultiLineString',
coordinates: [
[
[ 100, 0 ], [ 2, 1 ], [ 3, 2 ], [ 40, 30 ]
], [
[ 100, 0 ], [ 20, 10 ], [ 30, 20 ], [ 40, 30 ]
]
]
};
this.multiPolyline.setCoordinatesFromGeoJSON(newAndExpectedGeoJSON);
expect(this.multiPolyline.toGeoJSON()).toEqual(newAndExpectedGeoJSON);
});
});
});
@@ -0,0 +1,58 @@
var Point = require('../../../../src/geo/geometry-models/point');
describe('src/geo/geometry-models/point', function () {
beforeEach(function () {
this.point = new Point({
latlng: [100, 200]
});
});
describe('.toGeoJSON', function () {
it('should generate the GeoJSON correctly', function () {
expect(this.point.toGeoJSON()).toEqual({
type: 'Point',
coordinates: [ 200, 100 ]
});
});
});
describe('.setCoordinatesFromGeoJSON', function () {
beforeEach(function () {
this.changeCallback = jasmine.createSpy('changeCallback');
this.point.on('change', this.changeCallback);
});
describe('when given a GeoJSON with the same coordinates', function () {
beforeEach(function () {
var newAndExpectedGeoJSON = this.point.toGeoJSON();
this.point.setCoordinatesFromGeoJSON(newAndExpectedGeoJSON);
});
it('should NOT trigger a "change" event', function () {
expect(this.changeCallback).not.toHaveBeenCalled();
});
});
describe('when given a GeoJSON with different coordinates', function () {
beforeEach(function () {
var newAndExpectedGeoJSON = {
type: 'Point',
coordinates: [ 0, 300 ]
};
this.point.setCoordinatesFromGeoJSON(newAndExpectedGeoJSON);
expect(this.point.toGeoJSON()).toEqual(newAndExpectedGeoJSON);
});
it('should trigger a "change" event', function () {
expect(this.changeCallback).toHaveBeenCalled();
});
it('should update the coordinates', function () {
expect(this.point.toGeoJSON()).toEqual({
type: 'Point',
coordinates: [ 0, 300 ]
});
});
});
});
});
@@ -0,0 +1,66 @@
var Polygon = require('../../../../src/geo/geometry-models/polygon');
describe('src/geo/geometry-models/polygon', function () {
beforeEach(function () {
this.polygon = new Polygon(null, {
latlngs: [
[-1, 1], [1, 2], [3, 4]
]
});
});
describe('.toGeoJSON', function () {
it('should generate the GeoJSON correctly', function () {
expect(this.polygon.toGeoJSON()).toEqual({
type: 'Polygon',
coordinates: [
[ [ 1, -1 ], [ 2, 1 ], [ 4, 3 ], [ 1, -1 ] ]
]
});
});
});
describe('.setCoordinatesFromGeoJSON', function () {
beforeEach(function () {
this.changeCallback = jasmine.createSpy('changeCallback');
this.polygon.on('change', this.changeCallback);
});
describe('when given a GeoJSON with the same coordinates', function () {
beforeEach(function () {
var newAndExpectedGeoJSON = this.polygon.toGeoJSON();
this.polygon.setCoordinatesFromGeoJSON(newAndExpectedGeoJSON);
});
it('should NOT trigger a "change" event', function () {
expect(this.changeCallback).not.toHaveBeenCalled();
});
});
describe('when given a GeoJSON with different coordinates', function () {
beforeEach(function () {
var newAndExpectedGeoJSON = {
type: 'Polygon',
coordinates: [
[ [ 0, 0 ], [ 10, 10 ], [ 20, 20 ], [ 0, 0 ] ]
]
};
this.polygon.setCoordinatesFromGeoJSON(newAndExpectedGeoJSON);
expect(this.polygon.toGeoJSON()).toEqual(newAndExpectedGeoJSON);
});
it('should trigger a "change" event', function () {
expect(this.changeCallback).toHaveBeenCalled();
});
it('should update the coordinates', function () {
expect(this.polygon.toGeoJSON()).toEqual({
type: 'Polygon',
coordinates: [
[ [ 0, 0 ], [ 10, 10 ], [ 20, 20 ], [ 0, 0 ] ]
]
});
});
});
});
});
@@ -0,0 +1,64 @@
var Polyline = require('../../../../src/geo/geometry-models/polyline');
describe('src/geo/geometry-models/polyline', function () {
beforeEach(function () {
this.polyline = new Polyline(null, {
latlngs: [
[-1, 1], [1, 2], [3, 4]
]
});
});
describe('.toGeoJSON', function () {
it('should generate the GeoJSON correctly', function () {
expect(this.polyline.toGeoJSON()).toEqual({
type: 'LineString',
coordinates: [
[ 1, -1 ], [ 2, 1 ], [ 4, 3 ]
]
});
});
});
describe('.setCoordinatesFromGeoJSON', function () {
beforeEach(function () {
this.changeCallback = jasmine.createSpy('changeCallback');
this.polyline.on('change', this.changeCallback);
});
describe('when given a GeoJSON with the same coordinates', function () {
beforeEach(function () {
var newAndExpectedGeoJSON = this.polyline.toGeoJSON();
this.polyline.setCoordinatesFromGeoJSON(newAndExpectedGeoJSON);
});
it('should NOT trigger a "change" event', function () {
expect(this.changeCallback).not.toHaveBeenCalled();
});
});
describe('when given a GeoJSON with different coordinates', function () {
beforeEach(function () {
var newAndExpectedGeoJSON = {
type: 'LineString',
coordinates: [
[ 0, 0 ], [ 1, 1 ], [ 2, 2 ]
]
};
this.polyline.setCoordinatesFromGeoJSON(newAndExpectedGeoJSON);
expect(this.polyline.toGeoJSON()).toEqual(newAndExpectedGeoJSON);
});
it('should trigger a "change" event', function () {
expect(this.changeCallback).toHaveBeenCalled();
});
it('should update the coordinates', function () {
expect(this.polyline.toGeoJSON()).toEqual({
type: 'LineString',
coordinates: [ [ 0, 0 ], [ 1, 1 ], [ 2, 2 ] ]
});
});
});
});
});
@@ -0,0 +1,15 @@
var _ = require('underscore');
var isCoordinateSimilar = function (coordinateA, coordinateB) {
return coordinateA + 0.1 > coordinateB &&
coordinateA - 0.1 < coordinateB;
};
module.exports = {
areCoordinatesSimilar: function (coordinatesA, coordinatesB) {
return _.every(coordinatesA, function (coordinate, index) {
return isCoordinateSimilar(coordinate.lat, coordinatesB[index].lat) &&
isCoordinateSimilar(coordinate.lng, coordinatesB[index].lng);
});
}
};
@@ -0,0 +1,69 @@
var _ = require('underscore');
var Backbone = require('backbone');
var Map = require('../../../../src/geo/map');
var CoordinatesComparator = require('./coordinates-comparator');
module.exports = function (MapViewBase) {
if (!MapViewBase) throw new Error('MapViewBase is required');
// We extend the specific MapView and add some methods to make
// testing easier
var MapView = MapViewBase.extend({
initialize: function () {
MapViewBase.prototype.initialize.apply(this, arguments);
this._markers = [];
this._paths = [];
},
addMarker: function (marker) {
MapViewBase.prototype.addMarker.apply(this, arguments);
this._markers.push(marker);
},
removeMarker: function (marker) {
MapViewBase.prototype.removeMarker.apply(this, arguments);
var index = this._markers.indexOf(marker);
if (index >= 0) {
this._markers.splice(index, 1);
}
},
addPath: function (path) {
MapViewBase.prototype.addPath.apply(this, arguments);
this._paths.push(path);
},
removePath: function (path) {
MapViewBase.prototype.removePath.apply(this, arguments);
var index = this._paths.indexOf(path);
if (index >= 0) {
this._paths.splice(index, 1);
}
},
getMarkers: function () {
return this._markers;
},
getPaths: function () {
return this._paths;
},
findMarkerByLatLng: function (latlng) {
var markers = this.getMarkers();
return _.find(markers, function (marker) {
return CoordinatesComparator.areCoordinatesSimilar([marker.getCoordinates()], [latlng]);
}, this);
}
});
var map = new Map(null, {
layersFactory: {}
});
return new MapView({
mapModel: map,
engine: new Backbone.Model(),
layerGroupModel: {}
});
};
@@ -0,0 +1,7 @@
var GMapsMapView = require('../../../../../src/geo/gmaps/gmaps-map-view.js');
var GMapsMultiPointView = require('../../../../../src/geo/geometry-views/gmaps/multi-point-view.js');
var SharedTestsForMultiPointViews = require('../shared-tests-for-multi-point-views');
describe('src/geo/geometry-views/gmaps/multi-point-view.js', function () {
SharedTestsForMultiPointViews.call(this, GMapsMapView, GMapsMultiPointView);
});
@@ -0,0 +1,7 @@
var GMapsMapView = require('../../../../../src/geo/gmaps/gmaps-map-view.js');
var GMapsMultiPolygonView = require('../../../../../src/geo/geometry-views/gmaps/multi-polygon-view.js');
var SharedTestsForMultiPolygonViews = require('../shared-tests-for-multi-polygon-views');
describe('src/geo/geometry-views/gmaps/multi-polygon-view.js', function () {
SharedTestsForMultiPolygonViews.call(this, GMapsMapView, GMapsMultiPolygonView);
});
@@ -0,0 +1,7 @@
var GMapsMapView = require('../../../../../src/geo/gmaps/gmaps-map-view.js');
var GMapsMultiPolylineView = require('../../../../../src/geo/geometry-views/gmaps/multi-polyline-view.js');
var SharedTestsForMultiPolylineViews = require('../shared-tests-for-multi-polyline-views');
describe('src/geo/geometry-views/gmaps/multi-polygon-view.js', function () {
SharedTestsForMultiPolylineViews.call(this, GMapsMapView, GMapsMultiPolylineView);
});
@@ -0,0 +1,7 @@
var GMapsMapView = require('../../../../../src/geo/gmaps/gmaps-map-view.js');
var GMapsPointView = require('../../../../../src/geo/geometry-views/gmaps/point-view.js');
var SharedTestsForPointViews = require('../shared-tests-for-point-views');
describe('src/geo/geometry-views/gmaps/point-view.js', function () {
SharedTestsForPointViews.call(this, GMapsMapView, GMapsPointView);
});
@@ -0,0 +1,7 @@
var GMapsMapView = require('../../../../../src/geo/gmaps/gmaps-map-view.js');
var GMapsPolygonView = require('../../../../../src/geo/geometry-views/gmaps/polygon-view.js');
var SharedTestsForPolygonViews = require('../shared-tests-for-polygon-views');
describe('src/geo/geometry-views/gmaps/polygon-view.js', function () {
SharedTestsForPolygonViews.call(this, GMapsMapView, GMapsPolygonView);
});
@@ -0,0 +1,7 @@
var GMapsMapView = require('../../../../../src/geo/gmaps/gmaps-map-view.js');
var GMapsPolylineView = require('../../../../../src/geo/geometry-views/gmaps/polyline-view.js');
var SharedTestsForPolylineViews = require('../shared-tests-for-polyline-views');
describe('src/geo/geometry-views/gmaps/polyline-view.js', function () {
SharedTestsForPolylineViews.call(this, GMapsMapView, GMapsPolylineView);
});
@@ -0,0 +1,7 @@
var LeafletMapView = require('../../../../../src/geo/leaflet/leaflet-map-view.js');
var LeafletMultiPointView = require('../../../../../src/geo/geometry-views/leaflet/multi-point-view.js');
var SharedTestsForMultiPointViews = require('../shared-tests-for-multi-point-views');
describe('src/geo/geometry-views/leaflet/multi-point-view.js', function () {
SharedTestsForMultiPointViews.call(this, LeafletMapView, LeafletMultiPointView);
});
@@ -0,0 +1,7 @@
var LeafletMapView = require('../../../../../src/geo/leaflet/leaflet-map-view.js');
var LeafletMultiPolygonView = require('../../../../../src/geo/geometry-views/leaflet/multi-polygon-view.js');
var SharedTestsForMultiPolygonViews = require('../shared-tests-for-multi-polygon-views');
describe('src/geo/geometry-views/leaflet/multi-polygon-view.js', function () {
SharedTestsForMultiPolygonViews.call(this, LeafletMapView, LeafletMultiPolygonView);
});
@@ -0,0 +1,7 @@
var LeafletMapView = require('../../../../../src/geo/leaflet/leaflet-map-view.js');
var LeafletMultiPolylineView = require('../../../../../src/geo/geometry-views/leaflet/multi-polyline-view.js');
var SharedTestsForMultiPolylineViews = require('../shared-tests-for-multi-polyline-views');
describe('src/geo/geometry-views/leaflet/multi-polyline-view.js', function () {
SharedTestsForMultiPolylineViews.call(this, LeafletMapView, LeafletMultiPolylineView);
});
@@ -0,0 +1,7 @@
var LeafletMapView = require('../../../../../src/geo/leaflet/leaflet-map-view.js');
var LeafletPointView = require('../../../../../src/geo/geometry-views/leaflet/point-view.js');
var SharedTestsForPointViews = require('../shared-tests-for-point-views');
describe('src/geo/geometry-views/leaflet/point-view.js', function () {
SharedTestsForPointViews.call(this, LeafletMapView, LeafletPointView);
});
@@ -0,0 +1,7 @@
var LeafletMapView = require('../../../../../src/geo/leaflet/leaflet-map-view.js');
var LeafletPolygonView = require('../../../../../src/geo/geometry-views/leaflet/polygon-view.js');
var SharedTestsForPolygonViews = require('../shared-tests-for-polygon-views');
describe('src/geo/geometry-views/leaflet/polygon-view.js', function () {
SharedTestsForPolygonViews.call(this, LeafletMapView, LeafletPolygonView);
});
@@ -0,0 +1,7 @@
var LeafletMapView = require('../../../../../src/geo/leaflet/leaflet-map-view.js');
var LeafletPolylineView = require('../../../../../src/geo/geometry-views/leaflet/polyline-view.js');
var SharedTestsForPolylineViews = require('../shared-tests-for-polyline-views');
describe('src/geo/geometry-views/leaflet/polyline-view.js', function () {
SharedTestsForPolylineViews.call(this, LeafletMapView, LeafletPolylineView);
});
@@ -0,0 +1,31 @@
var _ = require('underscore');
module.exports = function () {
beforeEach(function () {
spyOn(_, 'debounce').and.callFake(function (func) { return function () { func.apply(this, arguments); }; });
this.geometryView.render();
});
describe('when the model is removed', function () {
it('should remove each geometry', function () {
this.geometry.geometries.each(function (polygon) {
spyOn(polygon, 'remove');
});
this.geometry.remove();
expect(this.geometry.geometries.all(function (geometry) {
return geometry.remove.calls.count() === 1;
})).toBe(true);
});
it('should remove the view', function () {
spyOn(this.geometryView, 'remove');
this.geometry.remove();
expect(this.geometryView.remove).toHaveBeenCalled();
});
});
};
@@ -0,0 +1,27 @@
var MultiPoint = require('../../../../src/geo/geometry-models/multi-point');
var SharedTestsForMultiGeometryViews = require('./shared-tests-for-multi-geometry-views');
var createMapView = require('./create-map-view');
module.exports = function (MapView, MultiPointView) {
beforeEach(function () {
this.geometry = new MultiPoint(null, {
latlngs: [
[0, 1],
[1, 2]
]
});
this.mapView = createMapView(MapView);
this.mapView.render();
this.geometryView = new MultiPointView({
model: this.geometry,
mapView: this.mapView
});
});
SharedTestsForMultiGeometryViews.call(this);
it('should render the geometries', function () {
expect(this.mapView.getMarkers().length).toEqual(2); // 2 points
});
};
@@ -0,0 +1,38 @@
var MultiPolygon = require('../../../../src/geo/geometry-models/multi-polygon');
var SharedTestsForMultiGeometryViews = require('./shared-tests-for-multi-geometry-views');
var createMapView = require('./create-map-view');
module.exports = function (MapView, MultiPolygonView) {
beforeEach(function () {
this.geometry = new MultiPolygon(null, {
latlngs: [
[
[0, 1],
[1, 2],
[2, 3],
[3, 4]
],
[
[0, 10],
[10, 20],
[20, 30],
[30, 40]
]
]
});
this.mapView = createMapView(MapView);
this.mapView.render();
this.geometryView = new MultiPolygonView({
model: this.geometry,
mapView: this.mapView
});
});
SharedTestsForMultiGeometryViews.call(this);
it('should render the geometries', function () {
expect(this.mapView.getPaths().length).toEqual(2); // 2 geometries
expect(this.mapView.getMarkers().length).toEqual(8); // 4 markers for each geometry
});
};
@@ -0,0 +1,38 @@
var MultiPolyline = require('../../../../src/geo/geometry-models/multi-polyline');
var SharedTestsForMultiGeometryViews = require('./shared-tests-for-multi-geometry-views');
var createMapView = require('./create-map-view');
module.exports = function (MapView, MultiPolylineView) {
beforeEach(function () {
this.geometry = new MultiPolyline(null, {
latlngs: [
[
[0, 1],
[1, 2],
[2, 3],
[3, 4]
],
[
[0, 10],
[10, 20],
[20, 30],
[30, 40]
]
]
});
this.mapView = createMapView(MapView);
this.mapView.render();
this.geometryView = new MultiPolylineView({
model: this.geometry,
mapView: this.mapView
});
});
SharedTestsForMultiGeometryViews.call(this);
it('should render the geometries', function () {
expect(this.mapView.getPaths().length).toEqual(2); // 2 geometries
expect(this.mapView.getMarkers().length).toEqual(8); // 4 markers for each geometry
});
};
@@ -0,0 +1,299 @@
var _ = require('underscore');
var Point = require('../../../../src/geo/geometry-models/point.js');
var createMapView = require('./create-map-view');
var CoordinatesComparator = require('./coordinates-comparator');
module.exports = function (Path, MapView, PathView) {
beforeEach(function () {
spyOn(_, 'debounce').and.callFake(function (func) { return function () { func.apply(this, arguments); }; });
this.mapView = createMapView(MapView);
this.mapView.render();
this.geometry = new Path(null, {
latlngs: [
[-1, 1], [1, 2], [3, 4]
]
});
this.geometryView = new PathView({
model: this.geometry,
mapView: this.mapView
});
this.geometryView.render();
});
it('should render some markers and the path', function () {
var paths = this.mapView.getPaths();
var markers = this.mapView.getMarkers();
expect(paths.length).toEqual(1);
expect(markers.length).toEqual(3); // 3 markers
expect(markers[0].getCoordinates()).toEqual({ lat: -1, lng: 1 });
expect(markers[0].isDraggable()).toBe(false);
expect(markers[1].getCoordinates()).toEqual({ lat: 1, lng: 2 });
expect(markers[1].isDraggable()).toBe(false);
expect(markers[2].getCoordinates()).toEqual({ lat: 3, lng: 4 });
expect(markers[2].isDraggable()).toBe(false);
expect(paths[0].getCoordinates()).toEqual([
{ lat: -1, lng: 1 }, { lat: 1, lng: 2 }, { lat: 3, lng: 4 }
]);
});
it('should not render duplicated markers', function () {
var paths = this.mapView.getPaths();
var markers = this.mapView.getMarkers();
expect(paths.length).toEqual(1);
expect(markers.length).toEqual(3); // 3 markers
this.geometry.setCoordinates([
[-1, 1], [1, 2], [3, 4], [-1, 1]
]);
paths = this.mapView.getPaths();
markers = this.mapView.getMarkers();
expect(paths.length).toEqual(1);
expect(markers.length).toEqual(4); // 4 markers
});
describe('when the model is updated', function () {
describe('when a point changed', function () {
beforeEach(function () {
this.geometry.points.at(0).set('latlng', [ -45, 45 ]);
});
it("should update the path's latlng", function () {
expect(this.geometry.getCoordinates()).toEqual([
[ -45, 45 ], [ 1, 2 ], [ 3, 4 ]
]);
});
});
describe('when points are added', function () {
beforeEach(function () {
this.numberOfMarkersBefore = this.mapView.getMarkers().length;
var point = new Point({
latlng: [
-40,
40
]
});
this.geometry.addPoint(point);
});
it('should update the path\'s latlng', function () {
expect(this.geometry.getCoordinates()).toEqual([
[ -40, 40 ], [ -1, 1 ], [ 1, 2 ], [ 3, 4 ]
]);
});
it('should render a new marker', function () {
var numberOfMarkersAfter = this.mapView.getMarkers().length;
expect(numberOfMarkersAfter).toEqual(this.numberOfMarkersBefore + 1);
});
});
describe('when the point triggers a dblclick event', function () {
beforeEach(function () {
this.geometry.setCoordinates([
[ -10, 10 ], [ 10, 20 ], [ 30, 40 ]
]);
});
it('should remove the geometry point', function () {
var pointToDelete = new Point({
latlng: [ -40, 40 ]
});
this.geometry.addPoint(pointToDelete);
expect(this.geometry.getCoordinates()).toEqual([
[-40, 40], [ -10, 10 ], [ 10, 20 ], [ 30, 40 ]
]);
pointToDelete.trigger('dblclick');
expect(this.geometry.getCoordinates()).toEqual([
[ -10, 10 ], [ 10, 20 ], [ 30, 40 ]
]);
});
});
describe('when points are resetted,', function () {
beforeEach(function () {
this.geometry.setCoordinates([
[ -10, 10 ], [ 10, 20 ], [ 30, 40 ]
]);
});
it("should update the path's latlng", function () {
expect(this.geometry.getCoordinates()).toEqual([
[ -10, 10 ], [ 10, 20 ], [ 30, 40 ]
]);
});
it('should render the right number of markers', function () {
var numberOfMarkersAfter = this.mapView.getMarkers().length;
expect(numberOfMarkersAfter).toEqual(3);
});
});
});
describe('when the model is removed', function () {
it('should remove the markers and path from the map', function () {
var paths = this.mapView.getPaths();
var markers = this.mapView.getMarkers();
expect(paths.length).toEqual(1);
expect(markers.length).toEqual(3); // 3 markers
this.geometry.remove();
paths = this.mapView.getPaths();
markers = this.mapView.getMarkers();
expect(paths.length).toEqual(0);
expect(markers.length).toEqual(0);
});
it('should remove the view', function () {
spyOn(this.geometryView, 'remove');
this.geometry.remove();
expect(this.geometryView.remove).toHaveBeenCalled();
});
});
describe('expandable paths', function () {
beforeEach(function (done) {
this.mapView = createMapView(MapView);
this.mapView.render();
this.geometry = new Path({
editable: true,
expandable: true
}, {
latlngs: [
[0, 0], [10, 0], [10, 10], [0, 10]
]
});
this.geometryView = new PathView({
model: this.geometry,
mapView: this.mapView
});
// Listen for the map to be ready
this.mapView.onReady(function () {
this.geometryView.render();
// Marker that we'll interact with in the tests
this.middlePointMarker = this.mapView.findMarkerByLatLng({ lat: 5, lng: 0 });
var markers = this.mapView.getMarkers();
this.numberOfMarkersBefore = markers.length;
done();
}.bind(this));
});
describe('when the model is removed', function () {
it('should remove the markers, middle points, and path from the map', function () {
var paths = this.mapView.getPaths();
var markers = this.mapView.getMarkers();
expect(paths.length).not.toEqual(0);
expect(markers.length).not.toEqual(0);
this.geometry.remove();
paths = this.mapView.getPaths();
markers = this.mapView.getMarkers();
expect(paths.length).toEqual(0);
expect(markers.length).toEqual(0);
});
});
describe('when user mousedowns a middle point', function () {
beforeEach(function () {
expect(this.middlePointMarker.getIconURL()).toEqual(Point.MIDDLE_POINT_ICON_URL);
this.middlePointMarker.trigger('mousedown');
});
it('should add a vertex to the geometry at [5, 0]', function () {
var paths = this.mapView.getPaths();
expect(paths.length).toEqual(1);
// Coordinates have different precissions and we just check they are similar
expect(CoordinatesComparator.areCoordinatesSimilar(paths[0].getCoordinates(), [
{ lat: 0, lng: 0 }, { lat: 5, lng: 0 }, { lat: 10, lng: 0 }, { lat: 10, lng: 10 }, { lat: 0, lng: 10 }
])).toBeTruthy();
});
it('should change the icon of the middle point', function () {
expect(this.middlePointMarker.getIconURL()).toEqual(Point.DEFAULT_ICON_URL);
});
it('should add two middle points at [2.5] and [7.5, 0]', function () {
var markers = this.mapView.getMarkers();
expect(markers.length).toEqual(this.numberOfMarkersBefore + 2);
expect(this.mapView.findMarkerByLatLng({ lat: 2.5, lng: 0 })).toBeDefined();
expect(this.mapView.findMarkerByLatLng({ lat: 7.5, lng: 0 })).toBeDefined();
});
it('should remove the markers, middle points, and path from the map when the model is removed', function () {
var paths = this.mapView.getPaths();
var markers = this.mapView.getMarkers();
expect(paths.length).not.toEqual(0);
expect(markers.length).not.toEqual(0);
this.geometry.remove();
paths = this.mapView.getPaths();
markers = this.mapView.getMarkers();
expect(paths.length).toEqual(0);
expect(markers.length).toEqual(0);
});
describe('when user drags a middle point', function () {
beforeEach(function () {
// Simulate a drag and drop to [5, -5]
spyOn(this.middlePointMarker, 'getCoordinates').and.returnValue({ lat: 5, lng: -5 });
this.middlePointMarker.trigger('dragstart');
this.middlePointMarker.trigger('drag');
this.middlePointMarker.trigger('dragend');
});
it('should update the coordinates of the new vertex to [5, -5]', function () {
var paths = this.mapView.getPaths();
expect(paths.length).toEqual(1);
expect(paths[0].getCoordinates()).toEqual([
{ lat: 0, lng: 0 }, { lat: 5, lng: -5 }, { lat: 10, lng: 0 }, { lat: 10, lng: 10 }, { lat: 0, lng: 10 }
]);
});
it('should add two middle points at [2.5] and [7.5, 0]', function () {
var markers = this.mapView.getMarkers();
expect(markers.length).toEqual(this.numberOfMarkersBefore + 2); // Only two middle points have been added
expect(this.mapView.findMarkerByLatLng({ lat: 2.5, lng: -2.5 })).toBeDefined();
expect(this.mapView.findMarkerByLatLng({ lat: 7.5, lng: -2.5 })).toBeDefined();
});
it('should delete the new point after dblclick event', function () {
this.middlePointMarker.trigger('mousedown');
this.middlePointMarker.trigger('dblclick');
var paths = this.mapView.getPaths();
expect(paths[0].getCoordinates()).toEqual([
{ lat: 0, lng: 0 }, { lat: 10, lng: 0 }, { lat: 10, lng: 10 }, { lat: 0, lng: 10 }
]);
});
});
});
});
};
@@ -0,0 +1,191 @@
var _ = require('underscore');
var Point = require('../../../../src/geo/geometry-models/point.js');
var createMapView = require('./create-map-view');
module.exports = function (MapView, PointView) {
beforeEach(function () {
spyOn(_, 'debounce').and.callFake(function (func) { return function () { func.apply(this, arguments); }; });
this.point = new Point({
latlng: [
-40,
40
]
});
this.mapView = createMapView(MapView);
this.mapView.render();
this.pointView = new PointView({
model: this.point,
mapView: this.mapView
});
this.pointView.render();
});
it('should add a marker to the map', function () {
var markers = this.mapView.getMarkers();
expect(markers.length).toEqual(1);
expect(markers[0].getCoordinates()).toEqual({
lat: -40,
lng: 40
});
expect(markers[0].isDraggable()).toBe(false);
});
it('should add a marker to the map when the model gets a lat and lng', function () {
this.point = new Point();
this.mapView = createMapView(MapView);
this.mapView.render();
this.pointView = new PointView({
model: this.point,
mapView: this.mapView
});
this.pointView.render();
var markers = this.mapView.getMarkers();
expect(markers.length).toEqual(0);
this.point.set('latlng', [ -45, 45 ]);
markers = this.mapView.getMarkers();
expect(markers.length).toEqual(1);
});
describe('when the model is updated', function () {
it("should update the marker's latlng", function () {
var markers = this.mapView.getMarkers();
expect(markers.length).toEqual(1);
expect(markers[0].getCoordinates()).toEqual({
lat: -40,
lng: 40
});
this.point.set('latlng', [ -45, 45 ]);
markers = this.mapView.getMarkers();
expect(markers.length).toEqual(1);
expect(markers[0].getCoordinates()).toEqual({
lat: -45,
lng: 45
});
});
});
describe('when the model is removed', function () {
it('should remove the marker if model is removed', function () {
expect(this.mapView.getMarkers().length).toEqual(1);
this.point.remove();
expect(this.mapView.getMarkers().length).toEqual(0);
});
it('should remove the view', function () {
spyOn(this.pointView, 'remove');
this.point.remove();
expect(this.pointView.remove).toHaveBeenCalled();
});
});
describe('editable points', function () {
beforeEach(function () {
this.point = new Point({
latlng: [
-40,
40
],
editable: true
});
this.mapView = createMapView(MapView);
this.mapView.render();
this.pointView = new PointView({
model: this.point,
mapView: this.mapView
});
this.pointView.render();
this.marker = this.mapView.getMarkers()[0];
});
it('should add an editable marker to the map', function () {
expect(this.marker.isDraggable()).toBe(true);
});
it("should update model's latlng when the marker is dragged & dropped", function () {
spyOn(this.marker, 'getCoordinates').and.returnValue({
lat: -90,
lng: 90
});
this.marker.trigger('dragstart');
this.marker.trigger('drag');
this.marker.trigger('dragend');
expect(this.point.getCoordinates()).toEqual([ -90, 90 ]);
});
it("shouldn't update the marker's latlng while dragging", function () {
this.marker.trigger('dragstart');
this.point.set('latlng', [
-50,
50
]);
expect(this.marker.getCoordinates().lat).toEqual(-40);
expect(this.marker.getCoordinates().lng).toEqual(40);
this.marker.trigger('drag');
this.marker.trigger('dragend');
this.point.set('latlng', [
-50,
50
]);
expect(this.marker.getCoordinates().lat).toEqual(-50);
expect(this.marker.getCoordinates().lng).toEqual(50);
});
it('should bind marker events', function () {
var callback = jasmine.createSpy('callback');
var marker = this.mapView.getMarkers()[0];
this.pointView.on('mousedown', callback);
marker.trigger('mousedown');
expect(callback).toHaveBeenCalled();
});
it('should unbind marker events when the view is cleaned', function () {
var callback = jasmine.createSpy('callback');
var marker = this.mapView.getMarkers()[0];
this.pointView.on('mousedown', callback);
this.pointView.clean();
marker.trigger('mousedown');
expect(callback).not.toHaveBeenCalled();
});
});
describe('.clean', function () {
it('should remove the marker from the map', function () {
var markers = this.mapView.getMarkers();
expect(markers.length).toEqual(1);
this.pointView.clean();
markers = this.mapView.getMarkers();
expect(markers.length).toEqual(0);
});
});
};
@@ -0,0 +1,96 @@
var Polygon = require('../../../../src/geo/geometry-models/polygon');
var SharedTestsForPathViews = require('./shared-tests-for-path-views');
var createMapView = require('./create-map-view');
var CoordinatesComparator = require('./coordinates-comparator');
module.exports = function (MapView, PathView) {
SharedTestsForPathViews.call(this, Polygon, MapView, PathView);
describe('expandable polygons', function () {
beforeEach(function (done) {
this.mapView = createMapView(MapView);
this.mapView.render();
this.geometry = new Polygon({
editable: true,
expandable: true
}, {
latlngs: [
[0, 0], [10, 0], [10, 10], [0, 10]
]
});
this.geometryView = new PathView({
model: this.geometry,
mapView: this.mapView
});
// Listen for the map to be ready
this.mapView.onReady(function () {
this.geometryView.render();
done();
}.bind(this));
});
it('should render markers for each vertex, the path, and middle points', function () {
var paths = this.mapView.getPaths();
var markers = this.mapView.getMarkers();
expect(paths.length).toEqual(1);
expect(markers.length).toEqual(8); // 4 markers + 4 middle points
// Markers
expect(markers[0].getCoordinates()).toEqual({ lat: 0, lng: 0 });
expect(markers[0].isDraggable()).toBe(true);
expect(markers[1].getCoordinates()).toEqual({ lat: 10, lng: 0 });
expect(markers[1].isDraggable()).toBe(true);
expect(markers[2].getCoordinates()).toEqual({ lat: 10, lng: 10 });
expect(markers[2].isDraggable()).toBe(true);
expect(markers[3].getCoordinates()).toEqual({ lat: 0, lng: 10 });
expect(markers[3].isDraggable()).toBe(true);
// Middle points
// Coordinates have different precissions and we just check they are similar
expect(CoordinatesComparator.areCoordinatesSimilar(
[ markers[4].getCoordinates() ],
[ { lat: 5, lng: 0 } ])
).toBeTruthy();
expect(markers[4].isDraggable()).toBe(true);
expect(CoordinatesComparator.areCoordinatesSimilar(
[ markers[5].getCoordinates() ],
[ { lat: 10, lng: 5 } ])
).toBeTruthy();
expect(markers[5].isDraggable()).toBe(true);
expect(CoordinatesComparator.areCoordinatesSimilar(
[ markers[6].getCoordinates() ],
[ { lat: 5, lng: 10 } ])
).toBeTruthy();
expect(markers[6].isDraggable()).toBe(true);
expect(CoordinatesComparator.areCoordinatesSimilar(
[ markers[7].getCoordinates() ],
[ { lat: 0, lng: 5 } ])
).toBeTruthy();
expect(markers[7].isDraggable()).toBe(true);
expect(paths[0].getCoordinates()).toEqual([
{ lat: 0, lng: 0 }, { lat: 10, lng: 0 }, { lat: 10, lng: 10 }, { lat: 0, lng: 10 }
]);
});
it('should re-render middle points when map is zoomed', function () {
spyOn(this.mapView, 'removeMarker');
spyOn(this.mapView, 'addMarker');
this.mapView.trigger('zoomend');
expect(this.mapView.removeMarker.calls.count()).toEqual(4);
expect(this.mapView.addMarker.calls.count()).toEqual(4);
});
});
};
@@ -0,0 +1,90 @@
var Polyline = require('../../../../src/geo/geometry-models/polyline');
var SharedTestsForPathViews = require('./shared-tests-for-path-views');
var createMapView = require('./create-map-view');
var CoordinatesComparator = require('./coordinates-comparator');
module.exports = function (MapView, PathView) {
SharedTestsForPathViews.call(this, Polyline, MapView, PathView);
describe('expandable polylines', function () {
beforeEach(function (done) {
this.mapView = createMapView(MapView);
this.mapView.render();
this.geometry = new Polyline({
editable: true,
expandable: true
}, {
latlngs: [
[0, 0], [10, 0], [10, 10], [0, 10]
]
});
this.geometryView = new PathView({
model: this.geometry,
mapView: this.mapView
});
// Listen for the map to be ready
this.mapView.onReady(function () {
this.geometryView.render();
done();
}.bind(this));
});
it('should render markers for each vertex, the path, and middle points', function () {
var paths = this.mapView.getPaths();
var markers = this.mapView.getMarkers();
expect(paths.length).toEqual(1);
expect(markers.length).toEqual(7); // 4 markers + 3 middle points
// Markers
expect(markers[0].getCoordinates()).toEqual({ lat: 0, lng: 0 });
expect(markers[0].isDraggable()).toBe(true);
expect(markers[1].getCoordinates()).toEqual({ lat: 10, lng: 0 });
expect(markers[1].isDraggable()).toBe(true);
expect(markers[2].getCoordinates()).toEqual({ lat: 10, lng: 10 });
expect(markers[2].isDraggable()).toBe(true);
expect(markers[3].getCoordinates()).toEqual({ lat: 0, lng: 10 });
expect(markers[3].isDraggable()).toBe(true);
// Middle points
// Coordinates have different precissions and we just check they are similar
expect(CoordinatesComparator.areCoordinatesSimilar(
[ markers[4].getCoordinates() ],
[ { lat: 5, lng: 0 } ])
).toBeTruthy();
expect(markers[4].isDraggable()).toBe(true);
expect(CoordinatesComparator.areCoordinatesSimilar(
[ markers[5].getCoordinates() ],
[ { lat: 10, lng: 5 } ])
).toBeTruthy();
expect(markers[5].isDraggable()).toBe(true);
expect(CoordinatesComparator.areCoordinatesSimilar(
[ markers[6].getCoordinates() ],
[ { lat: 5, lng: 10 } ])
).toBeTruthy();
expect(markers[6].isDraggable()).toBe(true);
expect(paths[0].getCoordinates()).toEqual([
{ lat: 0, lng: 0 }, { lat: 10, lng: 0 }, { lat: 10, lng: 10 }, { lat: 0, lng: 10 }
]);
});
it('should re-render middle points when map is zoomed', function () {
spyOn(this.mapView, 'removeMarker');
spyOn(this.mapView, 'addMarker');
this.mapView.trigger('zoomend');
expect(this.mapView.removeMarker.calls.count()).toEqual(3);
expect(this.mapView.addMarker.calls.count()).toEqual(3);
});
});
};
@@ -0,0 +1,139 @@
/* global google */
var GoogleCartoDBLayerGroupClass = require('../../../../src/geo/gmaps/gmaps-cartodb-layer-group-view');
var cartoLayerGroupViewTests = require('../shared-tests-for-carto-layer-group');
var CartoDBLayer = require('../../../../src/geo/map/cartodb-layer');
var LayersCollection = require('../../../../src/geo/map/layers');
var CartoDBLayerGroup = require('../../../../src/geo/cartodb-layer-group');
describe('gmaps-cartodb-layer-group-view', function () {
/**
* Helper function used to get a google map in the shared tests.
*/
function createNativeMap (container) {
// Create a leaflet map inside a container
container.setAttribute('id', 'map');
container.style.height = '200px';
document.body.appendChild(container);
var googleMap = new google.maps.Map(document.getElementById('map'), {
zoom: 4,
center: { lat: 47.84808037632246, lng: 14.2822265625 }
});
return googleMap;
}
/**
* Helper function used to get the tiles in the shared tests
*/
function getTileUrl (layerGroupView) {
return layerGroupView.options.tiles[0].replace('{s}', '0');
}
/**
* Gmaps events and Leaflet events are different.
*/
var event = {
da: { x: 121.8125, y: 94.56249999999997 },
data: { name: 'fakeCityName', cartodb_id: 123 },
e: { type: 'mousemove' },
latLng: { lat: function () { return 42.48830197960228; }, lng: function () { return -8.701171875; } },
layer: 0,
pixel: { x: 243, y: 274 }
};
// -- Shared tests for carto layer group
cartoLayerGroupViewTests(createNativeMap, GoogleCartoDBLayerGroupClass, getTileUrl, event);
// -- Google maps specific tests
describe('GMapsCartoDBLayerGroupView', function () {
var nativeMap;
var container;
var cartoDbLayer0;
var cartoDbLayer1;
var layerGroupView0;
var layerGroupView1;
var layerGroupModelMock;
var layersCollection;
var engineMock = {
on: jasmine.createSpy('on')
};
var mapModelMock = {
on: jasmine.createSpy('on'),
isFeatureInteractivityEnabled: jasmine.createSpy('isFeatureInteractivityEnabled').and.returnValue(false)
};
beforeEach(function () {
container = document.createElement('div');
nativeMap = createNativeMap(container);
cartoDbLayer0 = new CartoDBLayer({}, { engine: engineMock });
cartoDbLayer1 = new CartoDBLayer({}, { engine: engineMock });
layersCollection = new LayersCollection([cartoDbLayer0, cartoDbLayer1]);
layerGroupModelMock = new CartoDBLayerGroup({
urls: {
'subdomains': [0, 1, 2, 3],
'tiles': 'http://{s}.ashbu.cartocdn.com/documentation/api/v1/map/0123456789/{layerIndexes}/{z}/{x}/{y}.png',
'grids': [
[
'http://0.ashbu.cartocdn.com/documentation/api/v1/map/0123456789/0/{z}/{x}/{y}.grid.json',
'http://1.ashbu.cartocdn.com/documentation/api/v1/map/0123456789/0/{z}/{x}/{y}.grid.json',
'http://2.ashbu.cartocdn.com/documentation/api/v1/map/0123456789/0/{z}/{x}/{y}.grid.json',
'http://3.ashbu.cartocdn.com/documentation/api/v1/map/0123456789/0/{z}/{x}/{y}.grid.json'
],
[
'http://0.ashbu.cartocdn.com/documentation/api/v1/map/0123456789/1/{z}/{x}/{y}.grid.json',
'http://1.ashbu.cartocdn.com/documentation/api/v1/map/0123456789/1/{z}/{x}/{y}.grid.json',
'http://2.ashbu.cartocdn.com/documentation/api/v1/map/0123456789/1/{z}/{x}/{y}.grid.json',
'http://3.ashbu.cartocdn.com/documentation/api/v1/map/0123456789/1/{z}/{x}/{y}.grid.json'
]
],
'attributes': [
'http://ashbu.cartocdn.com/documentation/api/v1/map/0123456789/0/attributes',
'http://ashbu.cartocdn.com/documentation/api/v1/map/0123456789/1/attributes'
]
},
indexOfLayersInWindshaft: [1, 2]
}, {
layersCollection: layersCollection
});
layerGroupView0 = new GoogleCartoDBLayerGroupClass(layerGroupModelMock, { nativeMap: nativeMap, mapModel: mapModelMock });
layerGroupView1 = new GoogleCartoDBLayerGroupClass(layerGroupModelMock, { nativeMap: nativeMap, mapModel: mapModelMock });
nativeMap.overlayMapTypes.push(layerGroupView0);
nativeMap.overlayMapTypes.push(layerGroupView1);
});
describe('._getOverlayIndex', function () {
it('returns the index of the given layour group', function () {
expect(layerGroupView0._getOverlayIndex()).toEqual(0);
expect(layerGroupView1._getOverlayIndex()).toEqual(1);
});
});
describe('._refreshView', function () {
it('sets the layer group in the same position', function () {
layerGroupView0._refreshView();
expect(nativeMap.overlayMapTypes.getAt(0)).toEqual(layerGroupView0);
expect(nativeMap.overlayMapTypes.getAt(1)).toEqual(layerGroupView1);
});
});
describe('.remove', function () {
it('removes the correct layer group', function () {
layerGroupView0.remove();
expect(nativeMap.overlayMapTypes.getLength()).toEqual(1);
expect(nativeMap.overlayMapTypes.getAt(0)).toEqual(layerGroupView1);
});
});
afterEach(function () {
document.body.removeChild(container);
});
});
});
+156
View File
@@ -0,0 +1,156 @@
/* global google */
var $ = require('jquery');
var Backbone = require('backbone');
var Map = require('../../../../src/geo/map');
var GoogleMapsMapView = require('../../../../src/geo/gmaps/gmaps-map-view');
describe('geo/gmaps/gmaps-map-view', function () {
var mapView;
var map;
var spy;
var container;
beforeEach(function () {
container = $('<div>').css('height', '200px');
map = new Map(null, {
layersFactory: {}
});
spyOn(map, 'setMapViewSize').and.callThrough();
spyOn(map, 'setPixelToLatLngConverter').and.callThrough();
spyOn(map, 'setLatLngToPixelConverter').and.callThrough();
mapView = new GoogleMapsMapView({
el: container,
mapModel: map,
engine: new Backbone.Model(),
layerGroupModel: new Backbone.Model()
});
mapView.render();
spy = jasmine.createSpyObj('spy', ['zoomChanged', 'centerChanged', 'scrollWheelChanged']);
map.bind('change:zoom', spy.zoomChanged);
map.bind('change:center', spy.centerChanged);
map.bind('change:scrollwheel', spy.scrollWheelChanged);
});
it('should change zoom', function () {
mapView._setZoom(null, 10);
expect(spy.zoomChanged).toHaveBeenCalled();
});
it('should disable gmaps dragging and double click zooming when the map has drag disabled', function () {
var container = $('<div>').css({
'height': '200px',
'width': '200px'
});
var map = new Map({
drag: false
}, {
layersFactory: {}
});
var mapView = new GoogleMapsMapView({
el: container,
mapModel: map,
engine: new Backbone.Model(),
layerGroupModel: new Backbone.Model()
});
mapView.render();
expect(mapView._gmapsMap.get('draggable')).toBeFalsy();
expect(mapView._gmapsMap.get('disableDoubleClickZoom')).toBeTruthy();
});
it('should change center and zoom when bounds are changed', function (done) {
var spy = jasmine.createSpy('change:center');
mapView.getSize = function () { return {x: 200, y: 200}; };
map.bind('change:center', spy);
spyOn(mapView, '_setCenter');
mapView._bindModel();
map.set({
'view_bounds_ne': [1, 1],
'view_bounds_sw': [-0.3, -1.2]
});
setTimeout(function () {
expect(mapView._setCenter).toHaveBeenCalled();
done();
}, 1000);
});
it('should "forward" a dragend event to the map model', function () {
var container = $('<div>').css({
'height': '200px',
'width': '200px'
});
var map = new Map({
drag: false
}, {
layersFactory: {}
});
var mapView = new GoogleMapsMapView({
el: container,
mapModel: map,
engine: new Backbone.Model(),
layerGroupModel: new Backbone.Model()
});
mapView.render();
spyOn(map, 'trigger');
spyOn(mapView, 'trigger');
google.maps.event.trigger(mapView._gmapsMap, 'dragend');
expect(map.trigger).toHaveBeenCalledWith('moveend', jasmine.any(Object));
map.trigger.calls.reset();
mapView.trigger.calls.reset();
google.maps.event.trigger(mapView._gmapsMap, 'zoom_changed');
expect(map.trigger).toHaveBeenCalledWith('moveend', jasmine.any(Object));
map.trigger.calls.reset();
mapView.trigger.calls.reset();
});
it('should set mapview size when bounds changes', function () {
google.maps.event.trigger(mapView._gmapsMap, 'bounds_changed');
expect(map.setMapViewSize).toHaveBeenCalled();
});
describe('converters', function () {
it('should set converters', function () {
expect(map.setPixelToLatLngConverter).toHaveBeenCalled();
expect(map.setLatLngToPixelConverter).toHaveBeenCalled();
expect(map._pixelToLatLngConverter).toBeDefined();
expect(map._latLngToPixelConverter).toBeDefined();
});
it('should call native methods', function () {
spyOn(mapView.projector, 'latLngToPixel').and.callThrough();
spyOn(mapView.projector, 'pixelToLatLng').and.callThrough();
var pixelToLatLng = map.pixelToLatLng();
pixelToLatLng({x: 0, y: 0});
expect(mapView.projector.pixelToLatLng).toHaveBeenCalled();
var latLngToPixel = map.latLngToPixel();
latLngToPixel([0, 0]);
expect(mapView.projector.latLngToPixel).toHaveBeenCalled();
});
});
describe('listeners', function () {
beforeEach(function (done) {
// Listen for the map to be ready
mapView.onReady(done);
});
it('sets isReady to true if idle event is triggered', function () {
expect(mapView._isReady).toBe(true);
});
});
});
@@ -0,0 +1,69 @@
var $ = require('jquery');
var Backbone = require('backbone');
var Map = require('../../../../src/geo/map');
var GoogleMapsMapView = require('../../../../src/geo/gmaps/gmaps-map-view');
var GMapsLayerViewFactory = require('../../../../src/geo/gmaps/gmaps-layer-view-factory');
var TorqueLayer = require('../../../../src/geo/map/torque-layer');
var MockFactory = require('../../../helpers/mockFactory');
var SharedTestsForTorqueLayer = require('../shared-tests-for-torque-layer');
var torque = require('torque.js');
var createEngine = require('../../fixtures/engine.fixture.js');
describe('geo/gmaps/gmaps-torque-layer-view', function () {
beforeEach(function () {
var container = $('<div>').css('height', '200px');
var engineMock = createEngine();
this.map = new Map(null, {
layersFactory: {}
});
spyOn(this.map, 'trigger');
this.view = new GoogleMapsMapView({
el: container,
mapModel: this.map,
engine: new Backbone.Model(),
layerViewFactory: new GMapsLayerViewFactory(),
layerGroupModel: new Backbone.Model(),
showLimitErrors: false
});
this.model = new TorqueLayer({
type: 'torque',
source: MockFactory.createAnalysisModel({ id: 'a0' }),
cartocss: '#test {}',
'torque-steps': 100
}, { engine: engineMock });
spyOn(torque, 'GMapsTorqueLayer').and.callThrough();
this.map.addLayer(this.model);
this.view = this.view._layerViews[this.model.cid];
});
SharedTestsForTorqueLayer.call(this);
it('should apply TorqueLayer initialize method on the extended view with a bunch of attrs', function () {
expect(torque.GMapsTorqueLayer).toHaveBeenCalled();
var attrs = torque.GMapsTorqueLayer.calls.argsFor(0)[0];
expect(attrs.cartocss).toEqual(jasmine.any(String));
});
describe('when GMapsTorqueLayer triggers tileError', function () {
it('should trigger error:limit in mapModel if showLimitErrors is true', function () {
this.view.showLimitErrors = true;
this.view.nativeTorqueLayer.fire('tileError');
var calls = this.map.trigger.calls.all();
var types = calls.map(function (call) {
return call.args[0];
});
expect(types).toContain('error:limit');
});
it('should not trigger error:limit in mapModel if showLimitErrors is false', function () {
this.view.nativeTorqueLayer.fire('tileError');
var calls = this.map.trigger.calls.all();
var types = calls.map(function (call) {
return call.args[0];
});
expect(types).not.toContain('error:limit');
});
});
});
+87
View File
@@ -0,0 +1,87 @@
// var $ = require('jquery');
// var CartoDBLayerGMaps = require('../../../../src/geo/gmaps/cartodb-layer-gmaps');
// describe('CartoDBLayerGMaps: Hide funcionality', function() {
// var div, map, cdb_layer;
// beforeEach(function() {
// div = document.createElement('div');
// div.setAttribute("id","map");
// div.style.height = "100px";
// div.style.width = "100px";
// map = new google.maps.Map(div, {
// center: new google.maps.LatLng(51.505, -0.09),
// disableDefaultUI: false,
// zoom: 13,
// mapTypeId: google.maps.MapTypeId.ROADMAP,
// mapTypeControl: false
// });
// cdb_layer = new CartoDBLayerGMaps({
// map: map,
// user_name:"examples",
// tile_style: 'test',
// table_name: 'earthquakes',
// query: "SELECT * FROM {{table_name}}",
// tile_style: "#{{table_name}}{marker-fill:#E25B5B}",
// opacity:0.8,
// interactivity: "cartodb_id, magnitude",
// featureOver: function(ev,latlng,pos,data) {},
// featureOut: function() {},
// featureClick: function(ev,latlng,pos,data) {},
// debug: true
// });
// map.overlayMapTypes.setAt(0, cdb_layer);
// });
// it('if hides layers should work', function(done) {
// setTimeout(function () {
// cdb_layer.hide();
// setTimeout(function() {
// var $tile = $(div).find("img[gtilekey]").first()
// , opacity = cdb_layer.options.opacity
// , before_opacity = cdb_layer.options.previous_opacity;
// expect(cdb_layer.visible).toBeFalsy();
// expect($tile.css("opacity")).toEqual('0');
// expect(opacity).toEqual(0);
// expect(before_opacity).not.toEqual(0);
// done();
// }, 500);
// }, 500);
// });
// it('If sets opacity to 0, layer should be visible', function(done) {
// setTimeout(function () {
// cdb_layer.setOpacity(0);
// expect(cdb_layer.options.visible).toBeTruthy();
// done();
// }, 500);
// });
// it('toggle layer from a visible state should work', function(done) {
// setTimeout(function () {
// cdb_layer.hide();
// visibility = cdb_layer.toggle();
// setTimeout(function() {
// var $tile = $(div).find("img[gtilekey]").first()
// , opacity = cdb_layer.options.opacity;
// expect(visibility).toBeTruthy();
// expect(cdb_layer.visible).toBeTruthy();
// expect($tile.css("opacity")).toEqual('0.99');
// expect(opacity).toEqual(0.99);
// done();
// }, 500);
// }, 500);
// });
// });
@@ -0,0 +1,48 @@
// var $ = require('jquery');
// var CartoDBLayerGMaps = require('../../../../src/geo/gmaps/cartodb-layer-gmaps');
// describe('CartoDBLayerGMaps: Interaction funcionality', function() {
// var div, map, cdb_layer;
// beforeEach(function() {
// div = document.createElement('div');
// div.setAttribute("id","map");
// div.style.height = "100px";
// div.style.width = "100px";
// map = new google.maps.Map(div, {
// center: new google.maps.LatLng(51.505, -0.09),
// disableDefaultUI: false,
// zoom: 13,
// mapTypeId: google.maps.MapTypeId.ROADMAP,
// mapTypeControl: false
// });
// cdb_layer = new CartoDBLayerGMaps({
// map: map,
// user_name:"examples",
// table_name: 'country_colors',
// tile_style: 'test',
// opacity:0.8,
// interactivity: "cartodb_id",
// debug: true,
// interaction: true
// });
// map.overlayMapTypes.setAt(0, cdb_layer);
// });
// it('If there is no interaction defined, shouldn\'t work and failed', function() {
// // Fake a mouseover
// $(div).trigger('mouseover');
// expect(cdb_layer._manageOnEvents).toThrow();
// // Fake a mouseout
// $(div).trigger('mouseout');
// expect(cdb_layer._manageOffEvents).toThrow();
// // Fake a click
// $(div).trigger('click');
// expect(cdb_layer._manageOffEvents).toThrow();
// });
// });
@@ -0,0 +1,73 @@
// var $ = require('jquery');
// var CartoDBLayerGMaps = require('../../../../src/geo/gmaps/cartodb-layer-gmaps');
// describe('CartoDBLayerGMaps: Opacity interaction', function() {
// var div, map, cdb_layer;
// beforeEach(function() {
// div = document.createElement('div');
// div.setAttribute("id","map");
// div.style.height = "100px";
// div.style.width = "100px";
// map = new google.maps.Map(div, {
// center: new google.maps.LatLng(51.505, -0.09),
// disableDefaultUI: false,
// zoom: 13,
// mapTypeId: google.maps.MapTypeId.ROADMAP,
// mapTypeControl: false
// });
// cdb_layer = new CartoDBLayerGMaps({
// map: map,
// user_name:"examples",
// table_name: 'earthquakes',
// query: "SELECT * FROM {{table_name}}",
// tile_style: "#{{table_name}}{marker-fill:#E25B5B}",
// opacity: 0.8,
// interactivity: "cartodb_id, magnitude",
// featureOver: function(ev,latlng,pos,data) {},
// featureOut: function() {},
// featureClick: function(ev,latlng,pos,data) {},
// debug: true
// });
// map.overlayMapTypes.setAt(0, cdb_layer);
// });
// xit('Layer opacity should be 0.8', function() {
// waits(500);
// runs(function () {
// var $layer = $(div).find("img[gtilekey]").first()
// , opacity = cdb_layer.options.opacity;
// expect(cdb_layer.options.visible).toBeTruthy();
// expect($layer.css("opacity")).toEqual('0.8');//opacity.toString());
// });
// });
// xit('Opacity shouldn\'t change if it is not visible', function() {
// waits(500);
// runs(function() {
// cdb_layer.hide();
// cdb_layer.setOpacity(0.3);
// map.overlayMapTypes.setAt(0, cdb_layer);
// });
// waits(500);
// runs(function () {
// var $layer = $(div).find("img[gtilekey]").first()
// , opacity = cdb_layer.options.opacity
// , before_opacity = cdb_layer.options.previous_opacity;
// expect(cdb_layer.options.visible).toBeFalsy();
// expect($layer.css("opacity")).toEqual('0.3');
// expect(before_opacity).toEqual(0.8);
// });
// });
// });
+86
View File
@@ -0,0 +1,86 @@
// var $ = require('jquery');
// var CartoDBLayerGMaps = require('../../../../src/geo/gmaps/cartodb-layer-gmaps');
// describe('CartoDBLayerGMaps: Show funcionality', function() {
// var div, map, cdb_layer;
// beforeEach(function() {
// div = document.createElement('div');
// div.setAttribute("id","map");
// div.style.height = "100px";
// div.style.width = "100px";
// map = new google.maps.Map(div, {
// center: new google.maps.LatLng(51.505, -0.09),
// disableDefaultUI: false,
// zoom: 13,
// mapTypeId: google.maps.MapTypeId.ROADMAP,
// mapTypeControl: false
// });
// cdb_layer = new CartoDBLayerGMaps({
// map: map,
// user_name:"examples",
// table_name: 'earthquakes',
// query: "SELECT * FROM {{table_name}}",
// tile_style: "#{{table_name}}{marker-fill:#E25B5B}",
// opacity:0.8,
// interactivity: "cartodb_id, magnitude",
// featureOver: function(ev,latlng,pos,data) {},
// featureOut: function() {},
// featureClick: function(ev,latlng,pos,data) {},
// debug: true
// });
// map.overlayMapTypes.setAt(0, cdb_layer);
// });
// it('If layer is visible, show shouldn\'t do anything', function(done) {
// setTimeout(function () {
// expect(cdb_layer.show).toThrow();
// var opacity = cdb_layer.options.opacity;
// expect(cdb_layer.options.visible).toBeTruthy();
// done();
// }, 500);
// });
// it('Shows layer after hide it', function(done) {
// setTimeout(function () {
// cdb_layer.hide();
// cdb_layer.show();
// expect(cdb_layer.options.visible).toBeTruthy();
// done();
// }, 500);
// });
// it('If hides layer and set an opacity greater than 0, layer shouln\'t be visible', function(done) {
// setTimeout(function () {
// cdb_layer.hide();
// cdb_layer.setOpacity(0.2);
// expect(cdb_layer.visible).toBeFalsy();
// done();
// }, 500);
// });
// it('toggle layer from hidden state should work', function(done) {
// setTimeout(function () {
// cdb_layer.show();
// visibility = cdb_layer.toggle();
// setTimeout(function() {
// var $tile = $(div).find("img[gtilekey]").first()
// , opacity = cdb_layer.options.opacity
// , before_opacity = cdb_layer.options.previous_opacity;
// expect(cdb_layer.visible).toBeFalsy();
// expect(visibility).toBeFalsy();
// expect($tile.css("opacity")).toEqual('0');
// expect(opacity).toEqual(0);
// expect(before_opacity).not.toEqual(0);
// done();
// }, 500);
// }, 500);
// });
// });
@@ -0,0 +1,50 @@
/* global L */
var cartoLayerGroupViewTests = require('../shared-tests-for-carto-layer-group');
var LeafletCartoDBLayerGroupView = require('../../../../src/geo/leaflet/leaflet-cartodb-layer-group-view');
describe('leaflet-cartodb-layer-group-view', function () {
/**
* Helper function used to get a map in the shared tests.
*/
function createNativeMap (container) {
// Create a leaflet map inside a container
container.setAttribute('id', 'map');
container.style.height = '200px';
document.body.appendChild(container);
return L.map('map').setView([47.84808037632246, 14.2822265625], 4);
}
/**
* Helper function used to get the tiles in the shared tests
*/
function getTileUrl (layerGroupView) {
return layerGroupView.leafletLayer._url.replace('{s}', '0');
}
/**
* Gmaps events and Leaflet events are different.
*/
var event = {
containerPoint: { x: 264, y: 309 },
data: {
name: 'fakeCityName',
cartodb_id: 123
},
e: {
clientX: 696,
clientY: 325,
type: 'mousemove'
},
latlng: {
lat: 42.293564192170095,
lng: -8.173828125000002
},
layer: 0,
layerPoint: { x: 264, y: 309 },
originalEvent: {},
target: {},
type: 'mouseMove'
};
cartoLayerGroupViewTests(createNativeMap, LeafletCartoDBLayerGroupView, getTileUrl, event);
});
@@ -0,0 +1,54 @@
var Backbone = require('backbone');
var LeafletLayerView = require('../../../../src/geo/leaflet/leaflet-layer-view');
LeafletLayerView.prototype._createLeafletLayer = function () {
return jasmine.createSpyObj('leafletLayer', ['redraw', 'on']);
};
describe('leaflet-layer-view', function () {
beforeEach(function () {
this.layerModel = new Backbone.Model();
spyOn(this.layerModel, 'bind');
spyOn(this.layerModel, 'unbind');
this.leafletMap = jasmine.createSpyObj('leafletMap', ['removeLayer']);
this.layerView = new LeafletLayerView(this.layerModel, {
nativeMap: this.leafletMap
});
this.leafletLayer = this.layerView.leafletLayer;
});
it('should have a model change binding', function () {
expect(this.layerModel.bind).toHaveBeenCalledWith('change', this.layerView._modelUpdated, this.layerView);
});
describe('.remove', function () {
beforeEach(function () {
spyOn(this.layerView, 'trigger');
spyOn(this.layerView, 'unbind');
this.layerView.remove();
});
it('should remove layer from leaflet map', function () {
expect(this.leafletMap.removeLayer).toHaveBeenCalledWith(this.leafletLayer);
});
it('should trigger remove event', function () {
expect(this.layerView.trigger).toHaveBeenCalledWith('remove', this.layerView);
});
it('should unbind', function () {
expect(this.layerModel.unbind).toHaveBeenCalledWith(null, null, this.layerView);
expect(this.layerView.unbind).toHaveBeenCalled();
});
});
describe('.reload', function () {
beforeEach(function () {
this.layerView.reload();
});
it('should redraw map', function () {
expect(this.leafletLayer.redraw).toHaveBeenCalled();
});
});
});
@@ -0,0 +1,253 @@
var $ = require('jquery');
var _ = require('underscore');
var Backbone = require('backbone');
var Map = require('../../../../src/geo/map');
var TileLayer = require('../../../../src/geo/map/tile-layer');
var CartoDBLayer = require('../../../../src/geo/map/cartodb-layer');
var PlainLayer = require('../../../../src/geo/map/plain-layer');
var LayersCollection = require('../../../../src/geo/map/layers');
var CartoDBLayerGroup = require('../../../../src/geo/cartodb-layer-group');
var LeafletMapView = require('../../../../src/geo/leaflet/leaflet-map-view');
var LeafletTiledLayerView = require('../../../../src/geo/leaflet/leaflet-tiled-layer-view');
var LeafletPlainLayerView = require('../../../../src/geo/leaflet/leaflet-plain-layer-view');
var createEngine = require('../../fixtures/engine.fixture.js');
describe('geo/leaflet/leaflet-map-view', function () {
var mapView;
var map;
var spy;
var container;
var layer;
var engineMock;
beforeEach(function () {
container = $('<div>').css({
'height': '200px',
'width': '200px'
});
map = new Map(null, {
layersFactory: {},
attribution: [ '© CARTO' ]
});
spyOn(map, 'setMapViewSize').and.callThrough();
spyOn(map, 'setPixelToLatLngConverter').and.callThrough();
spyOn(map, 'setLatLngToPixelConverter').and.callThrough();
this.layerGroupModel = new CartoDBLayerGroup({}, { layersCollection: new LayersCollection() });
spyOn(this.layerGroupModel, 'hasTileURLTemplates').and.returnValue(true);
spyOn(this.layerGroupModel, 'getTileURLTemplate').and.returnValue('http://documentation.cartodb.com/api/v1/map/90e64f1b9145961af7ba36d71b887dd2:0/0/{z}/{x}/{y}.png');
mapView = new LeafletMapView({
el: container,
mapModel: map,
engine: new Backbone.Model(),
layerGroupModel: this.layerGroupModel
});
mapView.render();
var layerURL = 'http://{s}.tiles.mapbox.com/v3/cartodb.map-1nh578vv/{z}/{x}/{y}.png';
layer = new TileLayer({ urlTemplate: layerURL }, { engine: {} });
spy = jasmine.createSpyObj('spy', ['zoomChanged', 'centerChanged', 'keyboardChanged', 'changed']);
map.bind('change:zoom', spy.zoomChanged);
map.bind('change:keyboard', spy.keyboardChanged);
map.bind('change:center', spy.centerChanged);
map.bind('change', spy.changed);
engineMock = createEngine();
});
it('should change bounds when center is set', function () {
var spy = jasmine.createSpy('change:view_bounds_ne');
spyOn(map, 'getViewBounds');
map.bind('change:view_bounds_ne', spy);
map.set('center', [10, 10]);
expect(spy).toHaveBeenCalled();
expect(map.getViewBounds).not.toHaveBeenCalled();
});
it('should allow adding a layer', function () {
map.addLayer(layer);
expect(map.layers.length).toEqual(1);
});
it('should add layers on reset', function () {
map.layers.reset([
layer
]);
expect(map.layers.length).toEqual(1);
});
it('should allow removing a layer', function () {
map.addLayer(layer);
map.removeLayer(layer);
expect(map.layers.length).toEqual(0);
expect(_.size(mapView._layerViews)).toEqual(0);
});
it('should allow removing a layer by index', function () {
map.addLayer(layer);
map.removeLayerAt(0);
expect(map.layers.length).toEqual(0);
});
it('should allow removing a layer by Cid', function () {
var cid = map.addLayer(layer);
map.removeLayerByCid(cid);
expect(map.layers.length).toEqual(0);
});
it('should create a TiledLayerView when the layer is Tiled', function () {
var lyr = map.addLayer(layer);
var layerView = mapView.getLayerViewByLayerCid(lyr);
expect(LeafletTiledLayerView.prototype.isPrototypeOf(layerView)).isPrototypeOf();
});
it('should create a PlainLayer when the layer is cartodb', function () {
layer = new PlainLayer(null, { engine: {} });
var lyr = map.addLayer(layer);
var layerView = mapView.getLayerViewByLayerCid(lyr);
expect(layerView.setQuery).not.toEqual(LeafletPlainLayerView);
});
it('should insert layers in specified order', function () {
var tileLayer = new TileLayer({ urlTemplate: 'http://tilelayer1.com' }, { engine: {} });
map.addLayer(tileLayer);
var tileLayer2 = new TileLayer({ urlTemplate: 'http://tilelayer2.com' }, { engine: {} });
map.addLayer(tileLayer2, { at: 0 });
expect(mapView.getLayerViewByLayerCid(tileLayer.cid).leafletLayer.options.zIndex).toEqual(1);
expect(mapView.getLayerViewByLayerCid(tileLayer2.cid).leafletLayer.options.zIndex).toEqual(0);
});
it('should remove all layers when map view is cleaned', function () {
var cartoDBLayer1 = new CartoDBLayer({ meta: { cartocss: '#layer {}' } }, { engine: engineMock });
var cartoDBLayer2 = new CartoDBLayer({ meta: { cartocss: '#layer {}' } }, { engine: engineMock });
var tileLayer = new TileLayer({ urlTemplate: 'test' }, { engine: {} });
map.addLayer(cartoDBLayer1);
map.addLayer(cartoDBLayer2);
map.addLayer(tileLayer);
expect(_.size(mapView._layerViews)).toEqual(3);
var layerGroupView = mapView.getLayerViewByLayerCid(cartoDBLayer1.cid);
spyOn(layerGroupView, 'remove');
var tileLayerView = mapView.getLayerViewByLayerCid(tileLayer.cid);
spyOn(tileLayerView, 'remove');
mapView.clean();
expect(_.size(mapView._layerViews)).toEqual(0);
expect(layerGroupView.remove).toHaveBeenCalled();
expect(tileLayerView.remove).toHaveBeenCalled();
expect(mapView.layerGroupModel).not.toBeDefined();
});
it("should not add a layer view when it can't be created", function () {
var layer = new TileLayer({type: 'cartojs-test'}, { engine: {} });
map.addLayer(layer);
expect(_.size(mapView._layerViews)).toEqual(0);
});
describe('attributions', function () {
it('should not render Leaflet attributions', function () {
var attributions = mapView.$el.find('.leaflet-control-attribution');
expect(attributions.text()).toEqual('');
});
});
it('should disable leaflet dragging and double click zooming when the map has drag disabled', function () {
var container = $('<div>').css({
'height': '200px',
'width': '200px'
});
var map = new Map({
drag: false
}, {
layersFactory: {}
});
var mapView = new LeafletMapView({
el: container,
mapModel: map,
engine: new Backbone.Model(),
layerGroupModel: new Backbone.Model()
});
mapView.render();
expect(mapView._leafletMap.dragging.enabled()).toBeFalsy();
expect(mapView._leafletMap.doubleClickZoom.enabled()).toBeFalsy();
});
it('should "forward" a dragend event to the map model', function () {
var container = $('<div>').css({
'height': '200px',
'width': '200px'
});
var map = new Map({
drag: false
}, {
layersFactory: {}
});
var mapView = new LeafletMapView({
el: container,
mapModel: map,
engine: new Backbone.Model(),
layerGroupModel: new Backbone.Model()
});
mapView.render();
spyOn(map, 'trigger');
mapView._leafletMap.fire('moveend');
expect(map.trigger).toHaveBeenCalledWith('moveend', jasmine.any(Object));
});
it('should update mapView size on resize', function () {
mapView._leafletMap.fire('resize');
expect(map.setMapViewSize).toHaveBeenCalled();
});
describe('converters', function () {
it('should set converters', function () {
expect(map.setPixelToLatLngConverter).toHaveBeenCalled();
expect(map.setLatLngToPixelConverter).toHaveBeenCalled();
expect(map._pixelToLatLngConverter).toBeDefined();
expect(map._latLngToPixelConverter).toBeDefined();
});
it('should call native methods', function () {
spyOn(mapView._leafletMap, 'latLngToContainerPoint').and.callThrough();
spyOn(mapView._leafletMap, 'containerPointToLatLng').and.callThrough();
var pixelToLatLng = map.pixelToLatLng();
pixelToLatLng({x: 0, y: 0});
expect(mapView._leafletMap.containerPointToLatLng).toHaveBeenCalled();
var latLngToPixel = map.latLngToPixel();
latLngToPixel([0, 0]);
expect(mapView._leafletMap.latLngToContainerPoint).toHaveBeenCalled();
});
});
describe('.invalidateSize', function () {
it('should invalidate size in Leaflet and "re-center"', function () {
spyOn(mapView._leafletMap, 'invalidateSize');
spyOn(mapView._leafletMap, 'setView');
var center = mapView.map.get('center');
var zoom = mapView.map.get('zoom');
mapView.invalidateSize();
expect(mapView._leafletMap.setView).toHaveBeenCalledWith(center, zoom, jasmine.any(Object));
expect(mapView._leafletMap.invalidateSize).toHaveBeenCalled();
});
});
});
@@ -0,0 +1,132 @@
var Backbone = require('backbone');
var LeafletTiledLayerView = require('../../../../src/geo/leaflet/leaflet-tiled-layer-view');
describe('leaflet-tiled-layer-view', function () {
describe('when does not support high resolution screens', function () {
beforeEach(function () {
this.layerModel = new Backbone.Model({
urlTemplate: 'http://whatever.com',
subdomains: 'a',
opacity: 1,
attribution: 'CARTO',
errorTileUrl: 'http://matall.in',
maxZoom: 10,
minZoom: 0,
tms: false
});
this.leafletMap = jasmine.createSpy('leafletMap');
this.layerView = new LeafletTiledLayerView(this.layerModel, {
nativeMap: this.leafletMap
});
});
it('should have options set correctly', function () {
expect(this.layerView.leafletLayer.options.subdomains).toEqual(['a']);
expect(this.layerView.leafletLayer._url).toBe('http://whatever.com');
expect(this.layerView.leafletLayer.options.attribution).toBe('CARTO');
expect(this.layerView.leafletLayer.options.errorTileUrl).toBe('http://matall.in');
expect(this.layerView.leafletLayer.options.maxZoom).toBe(10);
expect(this.layerView.leafletLayer.options.minZoom).toBe(0);
expect(this.layerView.leafletLayer.options.opacity).toBe(1);
expect(this.layerView.leafletLayer.options.tms).toBeFalsy();
});
describe('._modelUpdated', function () {
beforeEach(function () {
this.layerModel.set({
subdomains: ['b'],
urlTemplate: 'http://hello.com',
attribution: 'carto',
maxZoom: 12,
minZoom: 1,
tms: true
});
});
it('should update several attributes', function () {
expect(this.layerView.leafletLayer.options.subdomains).toEqual(['b']);
expect(this.layerView.leafletLayer.options.attribution).toBe('carto');
expect(this.layerView.leafletLayer.options.errorTileUrl).toBe('http://matall.in');
expect(this.layerView.leafletLayer.options.maxZoom).toBe(12);
expect(this.layerView.leafletLayer.options.minZoom).toBe(1);
expect(this.layerView.leafletLayer.options.opacity).toBe(1);
expect(this.layerView.leafletLayer.options.tms).toBeTruthy();
});
it('should update url', function () {
expect(this.layerView.leafletLayer._url).toBe('http://hello.com');
});
});
it('should trigger load and loading events', function () {
var loadCallback = jasmine.createSpy('loadCallback');
this.layerView.bind('load', loadCallback);
this.layerView.leafletLayer.fire('load');
expect(loadCallback).toHaveBeenCalled();
var loadingCallback = jasmine.createSpy('loadingCallback');
this.layerView.bind('loading', loadingCallback);
this.layerView.leafletLayer.fire('loading');
expect(loadingCallback).toHaveBeenCalled();
});
});
describe('when supports high resolution screens', function () {
beforeEach(function () {
this.layerModel = new Backbone.Model({
urlTemplate: 'http://whatever.com',
urlTemplate2x: 'http://whatever.com2x',
subdomains: 'a',
opacity: 1,
attribution: 'CARTO',
errorTileUrl: 'http://matall.in',
maxZoom: 10,
minZoom: 0,
tms: false
});
this.leafletMap = jasmine.createSpy('leafletMap');
this.layerView = new LeafletTiledLayerView(this.layerModel, {
nativeMap: this.leafletMap
});
});
describe('when is high resolution screen', function () {
beforeEach(function () {
spyOn(this.layerView, '_isHdpi').and.returnValue(true);
});
it('should update the model correctly if high resolution template is enabled', function () {
this.layerModel.set({
urlTemplate: 'http://hello.com',
urlTemplate2x: 'http://hello.com2x'
});
expect(this.layerView.leafletLayer._url).toBe('http://hello.com2x');
});
it('should update the model correctly if high resolution template is not enabled', function () {
this.layerModel.set({
urlTemplate: 'http://hello.com'
});
expect(this.layerView.leafletLayer._url).toBe('http://hello.com');
});
});
describe('when it is not a high resolution screen', function () {
beforeEach(function () {
spyOn(this.layerView, '_isHdpi').and.returnValue(false);
});
it('should update the model correctly if high resolution template is enabled', function () {
this.layerModel.set({
urlTemplate: 'http://hello.com',
urlTemplate2x: 'http://hello.com2x'
});
expect(this.layerView.leafletLayer._url).toBe('http://hello.com');
});
});
});
});
@@ -0,0 +1,101 @@
/* global L */
var $ = require('jquery');
var Backbone = require('backbone');
var Map = require('../../../../src/geo/map');
var LeafletMapView = require('../../../../src/geo/leaflet/leaflet-map-view');
var LeafletLayerViewFactory = require('../../../../src/geo/leaflet/leaflet-layer-view-factory');
var TorqueLayer = require('../../../../src/geo/map/torque-layer');
var MockFactory = require('../../../helpers/mockFactory');
var createEngine = require('../../fixtures/engine.fixture.js');
var SharedTestsForTorqueLayer = require('../shared-tests-for-torque-layer');
describe('geo/leaflet/leaflet-torque-layer-view', function () {
var engineMock;
beforeEach(function () {
var container = $('<div>').css({
'height': '200px',
'width': '200px'
});
engineMock = createEngine();
this.map = new Map(null, {
layersFactory: {}
});
spyOn(this.map, 'trigger');
this.mapView = new LeafletMapView({
el: container,
mapModel: this.map,
engine: new Backbone.Model(),
layerViewFactory: new LeafletLayerViewFactory(),
layerGroupModel: new Backbone.Model(),
showLimitErrors: false
});
this.mapView.render();
spyOn(L.TorqueLayer.prototype, 'initialize').and.callThrough();
this.model = new TorqueLayer({
source: MockFactory.createAnalysisModel({ id: 'a0' }),
cartocss: 'Map {}',
dynamic_cdn: 'dynamic-cdn-value'
}, { engine: engineMock });
this.map.addLayer(this.model);
this.view = this.mapView._layerViews[this.model.cid];
});
SharedTestsForTorqueLayer.call(this);
it('should reuse layer view', function () {
this.view.check = 'testing';
var newLayer = new TorqueLayer({
source: MockFactory.createAnalysisModel({ id: 'a0' }),
cartocss: 'Map {}',
dynamic_cdn: 'dynamic-cdn-value'
}, {
engine: engineMock
});
this.map.layers.reset([ newLayer ]);
expect(this.mapView._layerViews[newLayer.cid].model).toEqual(newLayer);
expect(this.mapView._layerViews[newLayer.cid].check).toEqual('testing');
});
it('should apply Leaflet TorqueLayer initialize method on the extended view with a bunch of attrs', function () {
expect(L.TorqueLayer.prototype.initialize).toHaveBeenCalled();
var attrs = L.TorqueLayer.prototype.initialize.calls.argsFor(0)[0];
expect(attrs.cartocss).toEqual(jasmine.any(String));
expect(attrs.dynamic_cdn).toEqual('dynamic-cdn-value');
expect(attrs.instanciateCallback).toEqual(jasmine.any(Function));
});
it("should update providers's templateURL when urls change", function () {
this.model.set('tileURLTemplates', [
'http://pepe.carto.com/{z}/{x}/{y}.torque',
'http://juan.carto.com/{z}/{x}/{y}.torque'
]);
expect(this.view.leafletLayer.provider.templateUrl).toEqual('http://pepe.carto.com/{z}/{x}/{y}.torque');
});
describe('when LeafletTorqueLayer triggers tileError', function () {
it('should trigger error:limit in mapModel if showLimitErrors is true', function () {
this.view.showLimitErrors = true;
this.view.nativeTorqueLayer.fire('tileError');
var calls = this.map.trigger.calls.all();
var types = calls.map(function (call) {
return call.args[0];
});
expect(types).toContain('error:limit');
});
it('should not trigger error:limit in mapModel if showLimitErrors is false', function () {
this.view.nativeTorqueLayer.fire('tileError');
var calls = this.map.trigger.calls.all();
var types = calls.map(function (call) {
return call.args[0];
});
expect(types).not.toContain('error:limit');
});
});
});
+257
View File
@@ -0,0 +1,257 @@
var $ = require('jquery');
var Backbone = require('backbone');
var createEngine = require('../fixtures/engine.fixture.js');
var Map = require('../../../src/geo/map');
var MapView = require('../../../src/geo/map-view');
var TileLayer = require('../../../src/geo/map/tile-layer');
var CartoDBLayer = require('../../../src/geo/map/cartodb-layer');
var CartoDBLayerGroup = require('../../../src/geo/cartodb-layer-group');
var LayerGroupModel = CartoDBLayerGroup;
var fakeLayerViewFactory = {
createLayerView: function () {
var layerView = new Backbone.View();
layerView.setCursor = jasmine.createSpy('setCursor');
spyOn(layerView, 'remove');
return layerView;
}
};
var MyMapView = MapView.extend({
getSize: function () { return { x: 1000, y: 1000 }; },
_getLayerViewFactory: function () {
return fakeLayerViewFactory;
},
_createNativeMap: function () {}
});
describe('core/geo/map-view', function () {
var engineMock;
beforeEach(function () {
this.container = $('<div>').css('height', '200px');
engineMock = createEngine();
this.map = new Map(null, {
layersFactory: {}
});
this.layerViewFactory = jasmine.createSpyObj('layerViewFactory', ['createLayerView']);
this.mapView = new MyMapView({
el: this.container,
mapModel: this.map,
engine: new Backbone.Model(),
layerGroupModel: new LayerGroupModel(null, {
layersCollection: this.map.layers
})
});
spyOn(this.mapView, 'setCursor');
spyOn(this.mapView, 'getNativeMap');
spyOn(this.mapView, '_addLayerToMap');
});
describe('.render', function () {
it('should add layer views to the map', function () {
var tileLayer = new TileLayer(null, { engine: {} });
var cartoDBLayer1 = new CartoDBLayer({}, { engine: engineMock });
var cartoDBLayer2 = new CartoDBLayer({}, { engine: engineMock });
this.map.layers.reset([tileLayer, cartoDBLayer1, cartoDBLayer2]);
this.mapView = new MyMapView({
el: this.container,
mapModel: this.map,
engine: new Backbone.Model(),
layerGroupModel: new LayerGroupModel(null, {
windshaftMap: this.windshaftMap,
layersCollection: this.map.layers
})
});
spyOn(this.mapView, 'getNativeMap');
spyOn(this.mapView, '_addLayerToMap');
this.mapView.render();
expect(this.mapView._addLayerToMap.calls.count()).toEqual(2);
expect(this.mapView.getLayerViewByLayerCid(cartoDBLayer1.cid)).toBeDefined();
expect(this.mapView.getLayerViewByLayerCid(cartoDBLayer2.cid)).toBeDefined();
expect(this.mapView.getLayerViewByLayerCid(tileLayer.cid)).toBeDefined();
// Both CartoDBLayer layers share the same layer view
expect(this.mapView.getLayerViewByLayerCid(cartoDBLayer1.cid)).toEqual(this.mapView.getLayerViewByLayerCid(cartoDBLayer2.cid));
// Tile Layer has a different layer view
expect(this.mapView.getLayerViewByLayerCid(tileLayer.cid)).not.toEqual(this.mapView.getLayerViewByLayerCid(cartoDBLayer1.cid));
});
});
describe('bindings to map', function () {
it('should fitBounds when view bounds change', function () {
spyOn(this.map, 'fitBounds');
this.map.setBounds([ [0, 0], [180, 180] ]);
expect(this.map.fitBounds).toHaveBeenCalled();
});
});
describe('bindings to map.layers', function () {
describe('when layers of map.layers are resetted', function () {
it('should group CartoDB layers into a single layerView and add one layerView for each non-CartoDB layer', function () {
var tileLayer = new TileLayer(null, { engine: {} });
var cartoDBLayer1 = new CartoDBLayer({}, { engine: engineMock });
var cartoDBLayer2 = new CartoDBLayer({}, { engine: engineMock });
this.map.layers.reset([tileLayer, cartoDBLayer1, cartoDBLayer2]);
expect(this.mapView._addLayerToMap.calls.count()).toEqual(2);
expect(this.mapView.getLayerViewByLayerCid(cartoDBLayer1.cid)).toBeDefined();
expect(this.mapView.getLayerViewByLayerCid(cartoDBLayer2.cid)).toBeDefined();
expect(this.mapView.getLayerViewByLayerCid(tileLayer.cid)).toBeDefined();
// Both CartoDBLayer layers share the same layer view
expect(this.mapView.getLayerViewByLayerCid(cartoDBLayer1.cid)).toEqual(this.mapView.getLayerViewByLayerCid(cartoDBLayer2.cid));
// Tile Layer has a different layer view
expect(this.mapView.getLayerViewByLayerCid(tileLayer.cid)).not.toEqual(this.mapView.getLayerViewByLayerCid(cartoDBLayer1.cid));
});
it('should trigger a "newLayerView" event for each new layerView', function () {
var callback = jasmine.createSpy('callback');
this.mapView.on('newLayerView', callback);
var tileLayer = new TileLayer(null, { engine: {} });
var cartoDBLayer1 = new CartoDBLayer({}, { engine: engineMock });
var cartoDBLayer2 = new CartoDBLayer({}, { engine: engineMock });
this.map.layers.reset([tileLayer, cartoDBLayer1, cartoDBLayer2]);
expect(callback.calls.count()).toEqual(2);
expect(callback.calls.argsFor(0)[0]).toEqual(this.mapView.getLayerViewByLayerCid(tileLayer.cid));
expect(callback.calls.argsFor(1)[0]).toEqual(this.mapView.getLayerViewByLayerCid(cartoDBLayer1.cid));
});
});
describe('when new layerModels are added to map.layers', function () {
it('should add a new layer view to the map', function () {
var layer1 = new CartoDBLayer({}, { engine: engineMock });
this.map.addLayer(layer1);
var layerViewForLayer1 = this.mapView.getLayerViewByLayerCid(layer1.cid);
expect(layerViewForLayer1).toBeDefined();
expect(Object.keys(this.mapView._layerViews).length).toEqual(1);
expect(this.mapView._addLayerToMap).toHaveBeenCalled();
});
it('should group CartoDB layers into a single layerView', function () {
var tileLayer = new TileLayer(null, { engine: {} });
var cartoDBLayer1 = new CartoDBLayer({}, { engine: engineMock });
var cartoDBLayer2 = new CartoDBLayer({}, { engine: engineMock });
this.map.addLayer(tileLayer);
expect(this.mapView._addLayerToMap).toHaveBeenCalled();
this.mapView._addLayerToMap.calls.reset();
this.map.addLayer(cartoDBLayer1);
expect(this.mapView._addLayerToMap).toHaveBeenCalled();
this.mapView._addLayerToMap.calls.reset();
this.map.addLayer(cartoDBLayer2);
expect(this.mapView._addLayerToMap).not.toHaveBeenCalled();
// There are only two layerViews cause the CartoDBLayers have been grouped
expect(Object.keys(this.mapView._layerViews).length).toEqual(3);
expect(this.mapView.getLayerViewByLayerCid(cartoDBLayer1.cid)).toBeDefined();
expect(this.mapView.getLayerViewByLayerCid(cartoDBLayer2.cid)).toBeDefined();
expect(this.mapView.getLayerViewByLayerCid(tileLayer.cid)).toBeDefined();
// Both CartoDBLayer layers share the same layer view
expect(this.mapView.getLayerViewByLayerCid(cartoDBLayer1.cid)).toEqual(this.mapView.getLayerViewByLayerCid(cartoDBLayer2.cid));
// Tile Layer has a different layer view
expect(this.mapView.getLayerViewByLayerCid(tileLayer.cid)).not.toEqual(this.mapView.getLayerViewByLayerCid(cartoDBLayer1.cid));
});
it('should trigger a "newLayerView" event for each new layerView', function () {
var callback = jasmine.createSpy('callback');
this.mapView.on('newLayerView', callback);
var tileLayer = new TileLayer(null, { engine: {} });
var cartoDBLayer1 = new CartoDBLayer({}, { engine: engineMock });
var cartoDBLayer2 = new CartoDBLayer({}, { engine: engineMock });
this.map.addLayer(tileLayer);
this.map.addLayer(cartoDBLayer1);
this.map.addLayer(cartoDBLayer2);
expect(callback.calls.count()).toEqual(2);
expect(callback.calls.argsFor(0)[0]).toEqual(this.mapView.getLayerViewByLayerCid(tileLayer.cid));
expect(callback.calls.argsFor(1)[0]).toEqual(this.mapView.getLayerViewByLayerCid(cartoDBLayer1.cid));
});
});
describe('when layerModels are removed from map.layers', function () {
it('should should remove the corresponding layerView for layers that are rendered individually (not grouped)', function () {
var tileLayer = new TileLayer(null, { engine: {} });
this.map.layers.reset([tileLayer]);
var tileLayerView = this.mapView.getLayerViewByLayerCid(tileLayer.cid);
expect(tileLayerView).toBeDefined();
this.map.layers.remove(tileLayer);
// View for the tileLayer has been removed
expect(tileLayerView.remove).toHaveBeenCalled();
expect(this.mapView.getLayerViewByLayerCid(tileLayer.cid)).not.toBeDefined();
});
it('should should only remove a group layerView when all grouped layerModels have been removed', function () {
var cartoDBLayer1 = new CartoDBLayer({}, { engine: engineMock });
var cartoDBLayer2 = new CartoDBLayer({}, { engine: engineMock });
this.map.layers.reset([cartoDBLayer1, cartoDBLayer2]);
expect(this.mapView.getLayerViewByLayerCid(cartoDBLayer1.cid)).toBeDefined();
expect(this.mapView.getLayerViewByLayerCid(cartoDBLayer2.cid)).toBeDefined();
expect(this.mapView.getLayerViewByLayerCid(cartoDBLayer1.cid)).toEqual(this.mapView.getLayerViewByLayerCid(cartoDBLayer2.cid));
var cartoDBLayerGroupView = this.mapView.getLayerViewByLayerCid(cartoDBLayer1.cid);
this.map.layers.remove(cartoDBLayer1);
// There's one more CartoDBLayer so the layer view has not been removed
// this.mapView.getLayerViewByLayerCid(cartoDBLayer1.cid)
expect(cartoDBLayerGroupView.remove).not.toHaveBeenCalled();
expect(this.mapView.getLayerViewByLayerCid(cartoDBLayer1.cid)).not.toBeDefined();
expect(this.mapView.getLayerViewByLayerCid(cartoDBLayer2.cid)).toBeDefined();
this.map.layers.remove(cartoDBLayer2);
// There's one more CartoDBLayer so the layer view has not been removed
expect(cartoDBLayerGroupView.remove).toHaveBeenCalled();
expect(this.mapView.getLayerViewByLayerCid(cartoDBLayer2.cid)).not.toBeDefined();
});
it('should be able to add a layer after removing it', function () {
var cartoDBLayer = new CartoDBLayer({}, { engine: engineMock });
this.map.layers.reset([cartoDBLayer]);
var cartodbLayerView = this.mapView.getLayerViewByLayerCid(cartoDBLayer.cid);
expect(cartodbLayerView).toBeDefined();
expect(this.mapView.getLayerViewByLayerCid(cartoDBLayer.cid)).toBeDefined();
// Remove the layer
this.map.layers.remove(cartoDBLayer);
// View for the cartodbLayer has been removed
expect(cartodbLayerView.remove).toHaveBeenCalled();
expect(this.mapView.getLayerViewByLayerCid(cartoDBLayer.cid)).not.toBeDefined();
this.map.layers.add(cartoDBLayer);
expect(this.mapView.getLayerViewByLayerCid(cartoDBLayer.cid)).toBeDefined();
});
});
});
});
+510
View File
@@ -0,0 +1,510 @@
var _ = require('underscore');
var Backbone = require('backbone');
var PlainLayer = require('../../../src/geo/map/plain-layer');
var CartoDBLayer = require('../../../src/geo/map/cartodb-layer');
var TorqueLayer = require('../../../src/geo/map/torque-layer');
var TileLayer = require('../../../src/geo/map/tile-layer');
var WMSLayer = require('../../../src/geo/map/wms-layer');
var GMapsBaseLayer = require('../../../src/geo/map/gmaps-base-layer');
var LayersFactory = require('../../../src/vis/layers-factory');
var AnalysisModel = require('../../../src/analysis/analysis-model');
var createEngine = require('../fixtures/engine.fixture.js');
var Point = require('../../../src/geo/geometry-models/point');
var Polyline = require('../../../src/geo/geometry-models/polyline');
var Polygon = require('../../../src/geo/geometry-models/polygon');
var fakeLayersFactory = new LayersFactory({ engine: new Backbone.Model(), windshaftSettings: {} });
var createFakeAnalysis = function (attrs) {
return new AnalysisModel(attrs, {
engine: {},
camshaftReference: {
getParamNamesForAnalysisType: function () {}
}
});
};
var Map = require('../../../src/geo/map');
describe('core/geo/map', function () {
var map;
var engineMock;
beforeEach(function () {
engineMock = createEngine();
this.map = map = new Map(null, { layersFactory: fakeLayersFactory });
});
describe('.initialize', function () {
it('should parse bounds and set attributes', function () {
var map = new Map({ bounds: [[0, 1], [2, 3]] }, { layersFactory: fakeLayersFactory });
expect(map.get('view_bounds_sw')).toEqual([0, 1]);
expect(map.get('view_bounds_ne')).toEqual([2, 3]);
expect(map.get('bounds')).toBeUndefined();
});
it('should set the center and zoom if no bounds are given', function () {
var map = new Map({
center: [41.40282319070747, 2.3435211181640625],
zoom: 10
},
{
layersFactory: fakeLayersFactory
});
expect(map.get('center')).toEqual([41.40282319070747, 2.3435211181640625]);
expect(map.get('original_center')).toEqual([41.40282319070747, 2.3435211181640625]);
expect(map.get('zoom')).toEqual(10);
});
it('should set the default center and zoom if no center and bounds are given', function () {
var map = new Map(null, {
layersFactory: fakeLayersFactory
});
expect(map.get('center')).toEqual(map.defaults.center);
expect(map.get('original_center')).toEqual(map.defaults.center);
expect(map.get('zoom')).toEqual(map.defaults.zoom);
});
it('should parse the center when given a string', function () {
var map = new Map({
center: '[41.40282319070747, 2.3435211181640625]'
}, { layersFactory: fakeLayersFactory });
expect(map.get('center')).toEqual([41.40282319070747, 2.3435211181640625]);
});
});
describe('.addLayer', function () {
it('should add a layer to the collection', function () {
var layer = new Backbone.Model();
map.addLayer(layer);
expect(map.layers.models).toEqual([layer]);
});
});
describe('.removeLayer', function () {
it('should allow removing a layer', function () {
var layer = new Backbone.Model();
map.addLayer(layer);
map.removeLayer(layer);
expect(map.layers.length).toEqual(0);
});
});
describe('.removeLayerAt', function () {
it('should remove a layer by index', function () {
var layer1 = new Backbone.Model();
var layer2 = new Backbone.Model();
map.addLayer(layer1);
map.addLayer(layer2);
expect(map.layers.models).toEqual([layer1, layer2]);
map.removeLayerAt(0);
expect(map.layers.models).toEqual([layer2]);
});
});
describe('.removeLayerByCid', function () {
it('should allow removing a layer by Cid', function () {
var layer1 = new Backbone.Model();
var layer2 = new Backbone.Model();
map.addLayer(layer1);
map.addLayer(layer2);
expect(map.layers.models).toEqual([layer1, layer2]);
map.removeLayerByCid(layer1.cid);
expect(map.layers.models).toEqual([layer2]);
});
});
describe('.enableScrollWheel & .disableScrollWheel', function () {
it('should enable/disable the scroll wheel', function () {
map.disableScrollWheel();
expect(map.get('scrollwheel')).toEqual(false);
map.enableScrollWheel();
expect(map.get('scrollwheel')).toEqual(true);
});
});
it('should raise only one change event on setBounds', function () {
var c = 0;
map.bind('change:view_bounds_ne', function () {
c++;
});
map.setBounds([[1, 2], [1, 2]]);
expect(c).toEqual(1);
});
it('should not change center or zoom when the bounds are not ok', function () {
var c = 0;
map.bind('change:center', function () {
c++;
});
map.setBounds([[1, 2], [1, 2]]);
expect(c).toEqual(0);
});
it('should not change bounds when map size is 0', function () {
map.set('zoom', 10);
var bounds = [[43.100982876188546, 35.419921875], [60.23981116999893, 69.345703125]];
map.fitBounds(bounds, { x: 0, y: 0 });
expect(map.get('zoom')).toEqual(10);
});
it('should adjust zoom to layer', function () {
expect(map.get('maxZoom')).toEqual(map.defaults.maxZoom);
expect(map.get('minZoom')).toEqual(map.defaults.minZoom);
var layer = new PlainLayer({ minZoom: 5, maxZoom: 25 }, { engine: {} });
map.layers.reset(layer);
expect(map.get('maxZoom')).toEqual(25);
expect(map.get('minZoom')).toEqual(5);
layer = new PlainLayer({ minZoom: '7', maxZoom: '31' }, { engine: {} });
map.layers.reset(layer);
expect(map.get('maxZoom')).toEqual(31);
expect(map.get('minZoom')).toEqual(7);
});
it("shouldn't set a NaN zoom", function () {
var layer = new PlainLayer({ minZoom: NaN, maxZoom: NaN }, { engine: {} });
map.layers.reset(layer);
expect(map.get('maxZoom')).toEqual(map.defaults.maxZoom);
expect(map.get('minZoom')).toEqual(map.defaults.minZoom);
});
it('should update the attributions of the map when layers are reset/added/removed', function () {
map = new Map(null, { layersFactory: fakeLayersFactory });
// Map has the default CartoDB attribution
expect(map.get('attribution')).toEqual([
'© <a href="https://carto.com/attributions" target="_blank">CARTO</a>'
]);
var layer1 = new CartoDBLayer({ attribution: 'attribution1' }, { engine: engineMock });
var layer2 = new CartoDBLayer({ attribution: 'attribution1' }, { engine: engineMock });
var layer3 = new CartoDBLayer({ attribution: 'wadus' }, { engine: engineMock });
var layer4 = new CartoDBLayer({ attribution: '' }, { engine: engineMock });
map.layers.reset([layer1, layer2, layer3, layer4]);
// Attributions have been updated removing duplicated and empty attributions
expect(map.get('attribution')).toEqual([
'attribution1',
'wadus',
'© <a href="https://carto.com/attributions" target="_blank">CARTO</a>'
]);
var layer = new CartoDBLayer({ attribution: 'attribution2' }, { engine: engineMock });
map.layers.add(layer);
// The attribution of the new layer has been appended before the default CartoDB attribution
expect(map.get('attribution')).toEqual([
'attribution1',
'wadus',
'attribution2',
'© <a href="https://carto.com/attributions" target="_blank">CARTO</a>'
]);
layer.set('attribution', 'new attribution');
// The attribution of the layer has been updated in the map
expect(map.get('attribution')).toEqual([
'attribution1',
'wadus',
'new attribution',
'© <a href="https://carto.com/attributions" target="_blank">CARTO</a>'
]);
map.layers.remove(layer);
expect(map.get('attribution')).toEqual([
'attribution1',
'wadus',
'© <a href="https://carto.com/attributions" target="_blank">CARTO</a>'
]);
// Addind a layer with the default attribution
layer = new CartoDBLayer({}, { engine: engineMock });
map.layers.add(layer, { at: 0 });
// Default CartoDB only appears once and it's the last one
expect(map.get('attribution')).toEqual([
'attribution1',
'wadus',
'© <a href="https://carto.com/attributions" target="_blank">CARTO</a>'
]);
});
describe('Layer action methods', function () {
beforeEach(function () {
this.map.layers.add(new CartoDBLayer({}, { engine: engineMock }));
this.map.layers.add(new PlainLayer(null, { engine: {} }));
});
describe('.moveCartoDBLayer', function () {
it('should provide a way to move CartoDB layers', function () {
expect(this.map.moveCartoDBLayer).toBeDefined();
});
it('should trigger an event in order to notify other elements', function () {
var moveCartoDBLayer = jasmine.createSpy('moveCartoDBLayer', {});
this.map.on('cartodbLayerMoved', moveCartoDBLayer);
this.map.moveCartoDBLayer(0, 1);
expect(moveCartoDBLayer).toHaveBeenCalled();
});
});
});
describe('Layer creation methods', function () {
var testCases = [
{
createMethod: 'createCartoDBLayer',
expectedLayerModelClass: CartoDBLayer,
expectedLayerModelType: 'CartoDB',
testAttributes: {
source: createFakeAnalysis(),
cartocss: 'else'
}
},
{
createMethod: 'createTorqueLayer',
expectedLayerModelClass: TorqueLayer,
expectedLayerModelType: 'torque',
testAttributes: {
source: createFakeAnalysis(),
cartocss: 'else'
}
},
{
createMethod: 'createTileLayer',
expectedLayerModelClass: TileLayer,
expectedLayerModelType: 'Tiled',
testAttributes: {
urlTemplate: 'http://example.com'
}
},
{
createMethod: 'createWMSLayer',
expectedLayerModelClass: WMSLayer,
expectedLayerModelType: 'WMS',
testAttributes: {
urlTemplate: 'http://example.com'
}
},
{
createMethod: 'createGMapsBaseLayer',
expectedLayerModelClass: GMapsBaseLayer,
expectedLayerModelType: 'GMapsBase',
testAttributes: {
baseType: 'http://example.com'
}
},
{
createMethod: 'createPlainLayer',
expectedLayerModelClass: PlainLayer,
expectedLayerModelType: 'Plain',
testAttributes: {
color: '#FABADA'
}
}
];
_.each(testCases, function (testCase) {
describe('.' + testCase.createMethod, function () {
it('should return a layer of the corresponding type', function () {
var layer = this.map[testCase.createMethod](testCase.testAttributes);
expect(layer instanceof testCase.expectedLayerModelClass).toBeTruthy();
});
it('should set the right type', function () {
var layer = this.map[testCase.createMethod](testCase.testAttributes);
expect(layer.get('type')).toEqual(testCase.expectedLayerModelType);
});
it('should add the layer model to the collection of layers', function () {
var layer = this.map[testCase.createMethod](testCase.testAttributes);
expect(this.map.layers.at(0)).toEqual(layer);
});
it('should add the layer model at the given position', function () {
var layer1 = this.map[testCase.createMethod](testCase.testAttributes);
var layer0 = this.map[testCase.createMethod](testCase.testAttributes, { at: 0 });
var layer2 = this.map[testCase.createMethod](testCase.testAttributes, { at: 2 });
expect(this.map.layers.at(0)).toEqual(layer0);
expect(this.map.layers.at(1)).toEqual(layer1);
expect(this.map.layers.at(2)).toEqual(layer2);
});
it('should remove the layer after it triggers a destroy event', function () {
spyOn(this.map, '_removeLayerModelFromCollection');
var layer = this.map[testCase.createMethod](testCase.testAttributes);
layer.collection = {};
var options = { silent: true };
layer.trigger('destroy', layer, layer.collection, options);
expect(this.map._removeLayerModelFromCollection).toHaveBeenCalledWith(layer, layer.collection, options);
});
});
}, this);
});
describe('.getLayerById', function () {
beforeEach(function () {
var layer1 = new CartoDBLayer({ id: 'xyz-123', attribution: 'attribution1' }, { engine: engineMock });
map.layers.reset(layer1);
});
it('should return the corresponding model for given id', function () {
expect(map.getLayerById('xyz-123')).toBeDefined();
expect(map.getLayerById('meh')).toBeUndefined();
});
});
describe('.disableInteractivity', function () {
it('should disable feature interactivity and popups', function () {
this.map.enablePopups();
this.map.enableFeatureInteractivity();
this.map.disableInteractivity();
expect(this.map.arePopupsEnabled()).toBeFalsy();
expect(this.map.isFeatureInteractivityEnabled()).toBeFalsy();
});
});
describe('.enableInteractivity', function () {
it('should enable feature interactivity and popups', function () {
this.map.disablePopups();
this.map.disableFeatureInteractivity();
this.map.enableInteractivity();
expect(this.map.arePopupsEnabled()).toBeTruthy();
expect(this.map.isFeatureInteractivityEnabled()).toBeTruthy();
});
});
describe('geometry management', function () {
_.each({
drawPoint: Point,
drawPolyline: Polyline,
drawPolygon: Polygon
}, function (ExpectedGeometryClass, methodUnderTest) {
it('.' + methodUnderTest + ' should trigger an event with the right geometry', function () {
var callback = jasmine.createSpy('callback');
this.map.on('enterDrawingMode', callback);
var geometry = this.map[methodUnderTest]();
expect(geometry instanceof ExpectedGeometryClass).toBeTruthy();
expect(callback).toHaveBeenCalledWith(geometry);
});
});
it('.stopDrawingGeometry should trigger an event', function () {
var callback = jasmine.createSpy('callback');
this.map.on('exitDrawingMode', callback);
this.map.stopDrawingGeometry();
expect(callback).toHaveBeenCalled();
});
it('.editGeometry should trigger and event with the right geometry', function () {
var callback = jasmine.createSpy('callback');
this.map.on('enterEditMode', callback);
var geometry = this.map.editGeometry({
type: 'Feature',
properties: {},
geometry: {
type: 'Point',
coordinates: [
-3.779296875,
40.245991504199026
]
}
});
expect(geometry instanceof Point).toBeTruthy();
expect(callback).toHaveBeenCalledWith(geometry);
});
it('.stopEditingGeometry should trigger an event', function () {
var callback = jasmine.createSpy('callback');
this.map.on('exitEditMode', callback);
this.map.stopEditingGeometry();
expect(callback).toHaveBeenCalled();
});
});
describe('.getEstimatedFeatureCount', function () {
it('should return the sum of the estimated feature count for each layer (1+2)', function () {
map = new Map(null, { layersFactory: fakeLayersFactory });
map.layers.getCartoDBLayers = function () {
return [{
getEstimatedFeatureCount: function () {
return 1;
}
},
{
getEstimatedFeatureCount: function () {
return 2;
}
}];
};
expect(map.getEstimatedFeatureCount()).toEqual(3);
});
it('should return the sum of the estimated feature count for each layer (3+0)', function () {
map = new Map(null, { layersFactory: fakeLayersFactory });
map.layers.getCartoDBLayers = function () {
return [{
getEstimatedFeatureCount: function () {
return 3;
}
},
{
getEstimatedFeatureCount: function () {
return 0;
}
}];
};
expect(map.getEstimatedFeatureCount()).toEqual(3);
});
it('should return undefined when some layer has no estimated feature count', function () {
map = new Map(null, { layersFactory: fakeLayersFactory });
map.layers.getCartoDBLayers = function () {
return [{
getEstimatedFeatureCount: function () {
return undefined;
}
},
{
getEstimatedFeatureCount: function () {
return 2;
}
}];
};
expect(map.getEstimatedFeatureCount()).toBeUndefined();
});
});
});
+240
View File
@@ -0,0 +1,240 @@
var _ = require('underscore');
var CartoDBLayer = require('../../../../src/geo/map/cartodb-layer');
var sharedTestsForInteractiveLayers = require('./shared-for-interactive-layers');
var createEngine = require('../../fixtures/engine.fixture.js');
describe('geo/map/cartodb-layer', function () {
var engineMock;
beforeEach(function () {
engineMock = createEngine();
});
sharedTestsForInteractiveLayers(CartoDBLayer);
it('should be type CartoDB', function () {
var layer = new CartoDBLayer({}, { engine: engineMock });
expect(layer.get('type')).toEqual('CartoDB');
});
it('should expose infowindow and tooltip properties', function () {
var layer = new CartoDBLayer({}, { engine: engineMock });
expect(layer.infowindow).toBeDefined();
expect(layer.tooltip).toBeDefined();
});
describe('vis reloading', function () {
var ATTRIBUTES = ['sql', 'source', 'sql_wrap', 'cartocss'];
_.each(ATTRIBUTES, function (attribute) {
it("should reload the vis when '" + attribute + "' attribute changes", function () {
var layer = new CartoDBLayer({}, { engine: engineMock });
layer.set(attribute, 'new_value');
expect(engineMock.reload).toHaveBeenCalled();
});
});
it('should reload the map just once when multiple attributes change', function () {
var layer = new CartoDBLayer({}, { engine: engineMock });
var newAttributes = {};
_.each(ATTRIBUTES, function (attr, index) {
newAttributes[attr] = 'new_value_' + index;
});
layer.set(newAttributes);
expect(engineMock.reload).toHaveBeenCalled();
expect(engineMock.reload.calls.count()).toEqual(1);
});
describe('popups changes', function () {
var layer;
beforeEach(function () {
layer = new CartoDBLayer({}, { engine: engineMock });
});
describe('infowindow bind', function () {
it('should reload the map when infowindow fields are reset', function () {
layer.infowindow.fields.reset([
{
'name': 'name',
'title': true,
'position': 1
}
]);
expect(engineMock.reload).toHaveBeenCalled();
});
it('should reload the map when infowindow fields are added', function () {
layer.infowindow.fields.add({
'name': 'name',
'title': true,
'position': 1
});
expect(engineMock.reload).toHaveBeenCalled();
});
it('should reload the map when infowindow fields are removed', function () {
layer.infowindow.fields.add({
'name': 'name',
'title': true,
'position': 1
});
engineMock.reload.calls.reset();
layer.infowindow.fields.add({
'name': 'name',
'title': true,
'position': 1
});
layer.infowindow.fields.remove(layer.infowindow.fields.at(0));
expect(engineMock.reload).toHaveBeenCalled();
});
});
describe('tooltip bind', function () {
it('should reload the map when tooltip fields are reset', function () {
layer.tooltip.fields.reset([
{
'name': 'hello',
'title': false,
'position': 1
}
]);
expect(engineMock.reload).toHaveBeenCalled();
});
it('should reload the map when tooltip fields are added', function () {
layer.tooltip.fields.add({
'name': 'pericamen',
'title': false,
'position': 1
});
expect(engineMock.reload).toHaveBeenCalled();
});
it('should reload the map when tooltip fields are removed', function () {
layer.tooltip.fields.add({
'name': 'rename',
'title': true,
'position': 1
});
engineMock.reload.calls.reset();
layer.tooltip.fields.add({
'name': 'name',
'title': true,
'position': 1
});
layer.tooltip.fields.remove(layer.tooltip.fields.at(0));
expect(engineMock.reload).toHaveBeenCalled();
});
});
});
});
describe('.isInteractive', function () {
var layer, infowindowSpy, tooltipSpy;
beforeEach(function () {
layer = new CartoDBLayer({}, { engine: engineMock });
infowindowSpy = spyOn(layer, '_hasInfowindowFields');
tooltipSpy = spyOn(layer, '_hasTooltipFields');
});
it('returns true if there are selected fields', function () {
infowindowSpy.and.returnValue(true);
tooltipSpy.and.returnValue(false);
expect(layer.isInteractive()).toBe(true);
infowindowSpy.and.returnValue(false);
tooltipSpy.and.returnValue(true);
expect(layer.isInteractive()).toBe(true);
});
it('returns true if there are selected fields', function () {
infowindowSpy.and.returnValue(false);
tooltipSpy.and.returnValue(false);
expect(layer.isInteractive()).toBe(false);
});
});
describe('.getInteractiveColumnNames', function () {
it("should return 'cartodb_id' and the names of the fields for infowindows and tooltips with no duplicates", function () {
this.layer = new CartoDBLayer({
infowindow: {
fields: [
{ name: 'a' },
{ name: 'b' }
]
},
tooltip: {
fields: [
{ name: 'b' },
{ name: 'c' }
]
}
}, { engine: engineMock });
expect(this.layer.getInteractiveColumnNames()).toEqual(['cartodb_id', 'a', 'b', 'c']);
});
it("should return the 'cartodb_id' if no fields are present", function () {
this.layer = new CartoDBLayer({
infowindow: {
fields: []
},
tooltip: {
fields: []
}
}, { engine: engineMock });
expect(this.layer.getInteractiveColumnNames()).toEqual(['cartodb_id']);
});
});
describe('.getEstimatedFeatureCount', function () {
var layer;
beforeEach(function () {
layer = new CartoDBLayer({}, { engine: engineMock });
});
it('should return undefined when there is no meta information', function () {
layer.set('meta', {
stats: {}
});
expect(layer.getEstimatedFeatureCount()).toBeUndefined();
});
it('should return the number of features extracted from the meta-information when the layer is visible', function () {
layer.show();
layer.set('meta', {
stats: {
estimatedFeatureCount: 27
}
});
expect(layer.getEstimatedFeatureCount()).toEqual(27);
});
it('should return the number of features extracted from the meta-information when the layer is invisible', function () {
layer.hide();
layer.set('meta', {
stats: {
estimatedFeatureCount: 27
}
});
expect(layer.getEstimatedFeatureCount()).toEqual(27);
});
});
});
@@ -0,0 +1,8 @@
var GMapsBaseLayer = require('../../../../src/geo/map/gmaps-base-layer');
describe('GMapsBaseLayer', function () {
it('should be type GMapsBase', function () {
var layer = new GMapsBaseLayer();
expect(layer.get('type')).toEqual('GMapsBase');
});
});
@@ -0,0 +1,106 @@
var InfowindowTemplate = require('../../../../src/geo/map/infowindow-template');
describe('geo/map/infowindow-template', function () {
describe('.update', function () {
beforeEach(function () {
this.callback = jasmine.createSpy('callback');
this.infowindowTemplate = new InfowindowTemplate({
fields: [
{ name: 'Name', title: true, position: 1 }
]
});
this.infowindowTemplate.fields.bind('reset', this.callback, this);
});
it('should reset the fields when new fields are given', function () {
this.infowindowTemplate.update({
fields: [
{ name: 'Name', title: true, position: 1 },
{ name: 'Description', title: false, position: 2 }
]
});
expect(this.infowindowTemplate.fields.toJSON()).toEqual([
{ name: 'Name', title: true, position: 1 },
{ name: 'Description', title: false, position: 2 }
]);
expect(this.callback).toHaveBeenCalled();
});
it('should NOT reset the fields when given the same fields', function () {
this.infowindowTemplate.update({
fields: [
{ name: 'Name', title: true, position: 1 }
]
});
expect(this.infowindowTemplate.fields.toJSON()).toEqual([
{ name: 'Name', title: true, position: 1 }
]);
expect(this.callback).not.toHaveBeenCalled();
});
it('should NOT reset the fields when given the same fields with different `position`', function () {
this.infowindowTemplate.update({
fields: [
{ name: 'Name', title: true, position: 0 }
]
});
expect(this.infowindowTemplate.fields.toJSON()).toEqual([
{ name: 'Name', title: true, position: 1 }
]);
expect(this.callback).not.toHaveBeenCalled();
});
it('should reset the fields when given the same fields with different `position` and order', function () {
this.infowindowTemplate.update({
fields: [
{ name: 'Name', title: true, position: 0 },
{ name: 'Description', title: true, position: 1 }
]
});
expect(this.callback).toHaveBeenCalled();
this.callback.calls.reset();
this.infowindowTemplate.update({
fields: [
{ name: 'Description', title: true, position: 0 },
{ name: 'Name', title: true, position: 1 }
]
});
expect(this.callback).toHaveBeenCalled();
});
it('should clone alternative names', function () {
var callback = jasmine.createSpy('callback');
this.infowindowTemplate.bind('change', callback);
var alternativeNames = {
'name': 'Nombre'
};
this.infowindowTemplate.update({
alternative_names: alternativeNames
});
expect(callback).toHaveBeenCalled();
callback.calls.reset();
// Original object with names is changed
alternativeNames.description = 'Descripción';
this.infowindowTemplate.update({
alternative_names: alternativeNames
});
// Change event has been triggered
expect(callback).toHaveBeenCalled();
});
});
});
@@ -0,0 +1,85 @@
var LayerModelBase = require('../../../../src/geo/map/layer-model-base');
var MyLayer = LayerModelBase.extend({});
describe('geo/map/layer-model-base.js', function () {
beforeEach(function () {
this.layer = new MyLayer();
});
describe('.remove', function () {
it('should trigger a destroy event with options', function () {
var callback = jasmine.createSpy('callback');
var collection = {};
var option = { option: 'one' };
this.layer.bind('destroy', callback);
this.layer.collection = collection;
this.layer.remove(option);
expect(callback).toHaveBeenCalledWith(this.layer, this.layer.collection, option);
});
});
describe('.update', function () {
it('should set the attributes', function () {
this.layer.update({
a: 1,
b: 2
});
expect(this.layer.get('a')).toEqual(1);
expect(this.layer.get('b')).toEqual(2);
});
});
describe('.show', function () {
it('should set the visible attribute to true', function () {
this.layer.set('visible', false);
this.layer.show();
expect(this.layer.get('visible')).toBeTruthy();
});
});
describe('.hide', function () {
it('should set the visible attribute to false', function () {
this.layer.set('visible', true);
this.layer.hide();
expect(this.layer.get('visible')).toBeFalsy();
});
});
describe('.toggle', function () {
it('should toggel the visible attribute', function () {
this.layer.set('visible', false);
this.layer.toggle();
expect(this.layer.get('visible')).toBeTruthy();
this.layer.toggle();
expect(this.layer.get('visible')).toBeFalsy();
});
});
describe('.setOk', function () {
it('should unset error attribute', function () {
this.layer.set('error', 'error');
this.layer.setOk();
expect(this.layer.get('error')).toBeUndefined();
});
});
describe('.setError', function () {
it('should set error attribute', function () {
this.layer.setError('wadus');
expect(this.layer.get('error')).toEqual('wadus');
});
});
});
+164
View File
@@ -0,0 +1,164 @@
var Layers = require('../../../../src/geo/map/layers');
var PlainLayer = require('../../../../src/geo/map/plain-layer');
var TileLayer = require('../../../../src/geo/map/tile-layer');
var TorqueLayer = require('../../../../src/geo/map/torque-layer');
var CartoDBLayer = require('../../../../src/geo/map/cartodb-layer');
var createEngine = require('../../fixtures/engine.fixture.js');
describe('geo/map/layers', function () {
var layers;
var engineMock;
beforeEach(function () {
engineMock = createEngine();
layers = new Layers();
});
it('should re-assign order when new layers are added to the collection', function () {
var baseLayer = new TileLayer(null, { engine: {} });
var layer1 = new CartoDBLayer({}, { engine: engineMock });
var layer2 = new CartoDBLayer({}, { engine: engineMock });
var layer3 = new CartoDBLayer({}, { engine: engineMock });
// Sets the order to 0
layers.add(baseLayer);
expect(baseLayer.get('order')).toEqual(0);
// Sets the order to 1
layers.add(layer1);
layers.add(layer2);
expect(baseLayer.get('order')).toEqual(0);
expect(layer1.get('order')).toEqual(1);
expect(layer2.get('order')).toEqual(2);
expect(layers.pluck('order')).toEqual([ 0, 1, 2 ]);
// Sets the order to 1 and re-orders the rest of the layers
layers.add(layer3, { at: 1 });
expect(baseLayer.get('order')).toEqual(0);
expect(layer1.get('order')).toEqual(2);
expect(layer2.get('order')).toEqual(3);
expect(layer3.get('order')).toEqual(1);
expect(layers.pluck('order')).toEqual([ 0, 1, 2, 3 ]);
var torqueLayer = new TorqueLayer({}, { engine: engineMock });
// Torque layer should be at the top
layers.add(torqueLayer);
expect(baseLayer.get('order')).toEqual(0);
expect(layer1.get('order')).toEqual(2);
expect(layer2.get('order')).toEqual(3);
expect(layer3.get('order')).toEqual(1);
expect(torqueLayer.get('order')).toEqual(4);
expect(layers.pluck('order')).toEqual([ 0, 1, 2, 3, 4 ]);
var tiledLayer = new TileLayer(null, { engine: {} });
// Tiled layer should be at the top
layers.add(tiledLayer);
expect(baseLayer.get('order')).toEqual(0);
expect(layer1.get('order')).toEqual(2);
expect(layer2.get('order')).toEqual(3);
expect(layer3.get('order')).toEqual(1);
expect(torqueLayer.get('order')).toEqual(4);
expect(tiledLayer.get('order')).toEqual(5);
expect(layers.pluck('order')).toEqual([ 0, 1, 2, 3, 4, 5 ]);
var layer4 = new CartoDBLayer({}, { engine: engineMock });
layers.add(layer4);
expect(baseLayer.get('order')).toEqual(0);
expect(layer1.get('order')).toEqual(2);
expect(layer2.get('order')).toEqual(3);
expect(layer3.get('order')).toEqual(1);
expect(layer4.get('order')).toEqual(4);
expect(torqueLayer.get('order')).toEqual(5);
expect(tiledLayer.get('order')).toEqual(6);
expect(layers.pluck('order')).toEqual([ 0, 1, 2, 3, 4, 5, 6 ]);
});
it('should re-assign order when new layers are removed from the collection', function () {
var baseLayer = new TileLayer(null, { engine: {} });
var layer1 = new CartoDBLayer({}, { engine: engineMock });
var layer2 = new CartoDBLayer({}, { engine: engineMock });
var torqueLayer = new TorqueLayer({}, { engine: engineMock });
var labelsLayer = new TileLayer(null, { engine: {} });
// Sets the order to 0
layers.add(baseLayer);
layers.add(layer1);
layers.add(layer2);
layers.add(torqueLayer);
layers.add(labelsLayer);
expect(baseLayer.get('order')).toEqual(0);
expect(layer1.get('order')).toEqual(1);
expect(layer2.get('order')).toEqual(2);
expect(torqueLayer.get('order')).toEqual(3);
expect(labelsLayer.get('order')).toEqual(4);
expect(layers.pluck('order')).toEqual([ 0, 1, 2, 3, 4 ]);
layers.remove(layer1);
expect(baseLayer.get('order')).toEqual(0);
expect(layer2.get('order')).toEqual(1);
expect(torqueLayer.get('order')).toEqual(2);
expect(labelsLayer.get('order')).toEqual(3);
expect(layers.pluck('order')).toEqual([ 0, 1, 2, 3 ]);
layers.remove(torqueLayer);
expect(baseLayer.get('order')).toEqual(0);
expect(layer2.get('order')).toEqual(1);
expect(labelsLayer.get('order')).toEqual(2);
expect(layers.pluck('order')).toEqual([ 0, 1, 2 ]);
layers.remove(labelsLayer);
expect(baseLayer.get('order')).toEqual(0);
expect(layer2.get('order')).toEqual(1);
expect(layers.pluck('order')).toEqual([ 0, 1 ]);
});
describe('.moveCartoDBLayer', function () {
beforeEach(function () {
layers.add(new PlainLayer({ name: 'Positron' }, { engine: {} }));
layers.add(new CartoDBLayer({ title: 'CARTO' }, { engine: engineMock }));
});
it('should move a layer from one position to other', function () {
var movedLayer = layers.moveCartoDBLayer(1, 0);
expect(layers.indexOf(movedLayer)).toBe(0);
expect(movedLayer.get('title')).toBe('CARTO');
layers.add(new CartoDBLayer({ title: 'CARTO 2' }, { engine: engineMock }));
movedLayer = layers.moveCartoDBLayer(2, 1);
expect(layers.indexOf(movedLayer)).toBe(1);
expect(movedLayer.get('title')).toBe('CARTO 2');
});
it('should move a layer from one position to other', function () {
spyOn(layers, 'trigger');
var movedLayer = layers.moveCartoDBLayer(1, 0);
expect(layers.trigger).toHaveBeenCalledWith('layerMoved', movedLayer);
});
it('should not move anything if the position is the same', function () {
var movedLayer = layers.moveCartoDBLayer(1, 1);
expect(movedLayer).toBeFalsy();
expect(layers.at(1).get('title')).toBe('CARTO');
});
it('should not move the layer if it is not a CartoDB type', function () {
var movedLayer = layers.moveCartoDBLayer(0, 1);
expect(movedLayer).toBeFalsy();
expect(layers.at(1).get('title')).toBe('CARTO');
});
});
});
@@ -0,0 +1,24 @@
var LegendModelBase = require('../../../../../src/geo/map/legends/legend-model-base');
var createEngine = require('../../../fixtures/engine.fixture.js');
var Engine = require('../../../../../src/engine');
var MyLegendModel = LegendModelBase.extend({ TYPE: 'type' });
describe('src/geo/map/legends/legend-model-base', function () {
var engineMock;
var legendModel;
beforeEach(function () {
engineMock = createEngine();
legendModel = new MyLegendModel({}, { engine: engineMock });
});
it('should have a "loading" state', function () {
expect(legendModel.isLoading()).toBeTruthy();
});
it('should change state to "loading" when vis is reloading', function () {
legendModel.set('state', 'something');
expect(legendModel.isLoading()).toBeFalsy();
engineMock._eventEmmitter.trigger(Engine.Events.RELOAD_STARTED);
expect(legendModel.isLoading()).toBeTruthy();
});
});
+121
View File
@@ -0,0 +1,121 @@
var Legends = require('../../../../../src/geo/map/legends/legends');
var createEngine = require('../../../fixtures/engine.fixture.js');
describe('geo/map/legends/legends', function () {
var engineMock;
beforeEach(function () {
engineMock = createEngine();
this.legends = new Legends([
{
type: 'bubble',
title: 'Bubble Legend',
pre_html: '<p>PRE Bubble Legend</p>',
post_html: '<p>POST Bubble Legend</p>',
definition: {
color: '#FABADA'
}
},
{
type: 'category',
title: 'Categories Legend',
pre_html: '<p>PRE Category Legend</p>',
post_html: '<p>POST Category Legend</p>',
definition: {
prefix: 'prefix',
suffix: 'suffix'
}
},
{
type: 'choropleth',
title: 'Choropleth Legend',
pre_html: '<p>PRE Choropleth Legend</p>',
post_html: '<p>POST Choropleth Legend</p>',
definition: {
prefix: 'prefix',
suffix: 'suffix'
}
},
{
type: 'custom',
title: 'Custom Legend',
pre_html: '<p>PRE Custom Legend</p>',
post_html: '<p>POST Custom Legend</p>',
definition: {
categories: [
{ type: 'color', name: 'Item 1', color: '#CACACA;' },
{ type: 'color', name: 'Item 2', color: '#FABADA;' }
]
}
}
], {
engine: engineMock
});
});
it('should initialize bubble legends correctly', function () {
expect(this.legends.bubble.get('title')).toEqual('Bubble Legend');
expect(this.legends.bubble.get('preHTMLSnippet')).toEqual('<p>PRE Bubble Legend</p>');
expect(this.legends.bubble.get('postHTMLSnippet')).toEqual('<p>POST Bubble Legend</p>');
expect(this.legends.bubble.get('fillColor')).toEqual('#FABADA');
});
it('should initialize category legends correctly', function () {
expect(this.legends.category.get('title')).toEqual('Categories Legend');
expect(this.legends.category.get('preHTMLSnippet')).toEqual('<p>PRE Category Legend</p>');
expect(this.legends.category.get('postHTMLSnippet')).toEqual('<p>POST Category Legend</p>');
expect(this.legends.category.get('prefix')).toEqual('prefix');
expect(this.legends.category.get('suffix')).toEqual('suffix');
});
it('should initialize choropleth legends correctly', function () {
expect(this.legends.choropleth.get('title')).toEqual('Choropleth Legend');
expect(this.legends.choropleth.get('preHTMLSnippet')).toEqual('<p>PRE Choropleth Legend</p>');
expect(this.legends.choropleth.get('postHTMLSnippet')).toEqual('<p>POST Choropleth Legend</p>');
expect(this.legends.choropleth.get('prefix')).toEqual('prefix');
expect(this.legends.choropleth.get('suffix')).toEqual('suffix');
});
it('should initialize custom legends correctly', function () {
expect(this.legends.custom.get('title')).toEqual('Custom Legend');
expect(this.legends.custom.get('preHTMLSnippet')).toEqual('<p>PRE Custom Legend</p>');
expect(this.legends.custom.get('postHTMLSnippet')).toEqual('<p>POST Custom Legend</p>');
expect(this.legends.custom.get('items').length).toEqual(2);
});
it("it should 'show' all legends that have data", function () {
this.legends = new Legends([
{
type: 'bubble',
title: 'Bubble Legend',
pre_html: '<p>PRE Bubble Legend</p>',
post_html: '<p>POST Bubble Legend</p>',
definition: {
color: '#FABADA'
}
}
], {
engine: engineMock
});
expect(this.legends.bubble.isVisible()).toBeTruthy();
expect(this.legends.category.isVisible()).toBeFalsy();
this.legends = new Legends([
{
type: 'category',
title: 'Categories Legend',
pre_html: '<p>PRE Category Legend</p>',
post_html: '<p>POST Category Legend</p>',
definition: {
prefix: 'prefix',
suffix: 'suffix'
}
}
], {
engine: engineMock
});
expect(this.legends.bubble.isVisible()).toBeFalsy();
expect(this.legends.category.isVisible()).toBeTruthy();
});
});
@@ -0,0 +1,20 @@
var StaticLegendModelBase = require('../../../../../src/geo/map/legends/static-legend-model-base');
var createEngine = require('../../../fixtures/engine.fixture.js');
var Engine = require('../../../../../src/engine');
var MyLegendModel = StaticLegendModelBase.extend({ TYPE: 'type' });
describe('src/geo/map/legends/static-legend-model-base', function () {
var engineMock;
var legendModel;
beforeEach(function () {
engineMock = createEngine();
legendModel = new MyLegendModel({}, { engine: engineMock });
});
it('should never change state to "loading" when vis is reloading', function () {
legendModel.set('state', 'something');
expect(legendModel.isLoading()).toBeFalsy();
engineMock._eventEmmitter.trigger(Engine.Events.RELOAD_STARTED);
expect(legendModel.isLoading()).toBeFalsy();
});
});
+29
View File
@@ -0,0 +1,29 @@
var _ = require('underscore');
var PlainLayer = require('../../../../src/geo/map/plain-layer');
var createEngine = require('../../fixtures/engine.fixture.js');
describe('PlainLayer', function () {
var engineMock;
beforeEach(function () {
engineMock = createEngine();
});
it('should be type plain', function () {
var layer = new PlainLayer(null, { engine: engineMock });
expect(layer.get('type')).toEqual('Plain');
});
describe('vis reloading', function () {
var ATTRIBUTES = ['color', 'image'];
_.each(ATTRIBUTES, function (attribute) {
it("should reload the vis when '" + attribute + "' attribute changes", function () {
var layer = new PlainLayer(null, { engine: engineMock });
layer.set(attribute, 'new_value');
expect(engineMock.reload).toHaveBeenCalled();
});
});
});
});
@@ -0,0 +1,76 @@
var _ = require('underscore');
var MockFactory = require('../../../helpers/mockFactory');
var createEngine = require('../../fixtures/engine.fixture.js');
module.exports = function (LayerModel) {
var layer;
var source;
var engineMock;
beforeEach(function () {
source = MockFactory.createAnalysisModel({ id: 'a0' });
engineMock = createEngine();
layer = new LayerModel({source: source}, { engine: engineMock });
});
var METHODS = [
'isVisible',
'getName'
];
_.each(METHODS, function (method) {
it('should respond to .' + method, function () {
expect(typeof layer[method] === 'function').toBeTruthy();
});
});
it('should have legends', function () {
var legends = [
{ type: 'bubble', title: 'My Bubble Legend' },
{ type: 'category', title: 'My Category Legend' },
{ type: 'choropleth', title: 'My Choropleth Legend' },
{ type: 'custom', title: 'My Custom Legend' }
];
layer = new LayerModel({ legends: legends }, { engine: engineMock });
expect(layer.get('legends')).toBeUndefined();
expect(layer.legends.bubble.get('title')).toEqual('My Bubble Legend');
expect(layer.legends.category.get('title')).toEqual('My Category Legend');
expect(layer.legends.choropleth.get('title')).toEqual('My Choropleth Legend');
expect(layer.legends.custom.get('title')).toEqual('My Custom Legend');
});
describe('source references', function () {
describe('when layer is initialized', function () {
it('should mark source as referenced', function () {
expect(source.isSourceOf(layer)).toBe(true);
});
});
describe('when layer is updated', function () {
it('should unmark source and mark new source as referenced', function () {
var oldSource = source;
var newSource = MockFactory.createAnalysisModel({ id: 'a1' });
expect(oldSource.isSourceOf(layer)).toBe(true);
expect(newSource.isSourceOf(layer)).toBe(false);
layer.setSource(newSource);
expect(oldSource.isSourceOf(layer)).toBe(false);
expect(newSource.isSourceOf(layer)).toBe(true);
});
});
describe('when layer is removed', function () {
it('should unmark source as referenced', function () {
expect(source.isSourceOf(layer)).toBe(true);
layer.remove();
expect(source.isSourceOf(layer)).toBe(false);
});
});
});
};
+23
View File
@@ -0,0 +1,23 @@
var TileLayer = require('../../../../src/geo/map/tile-layer');
var createEngine = require('../../fixtures/engine.fixture.js');
describe('TileLayer', function () {
var engineMock;
var layer;
beforeEach(function () {
engineMock = createEngine();
layer = new TileLayer(null, { engine: engineMock });
});
it('should be type tiled', function () {
expect(layer.get('type')).toEqual('Tiled');
});
describe('vis reloading', function () {
it("should reload the vis when 'urlTemplate' attribute changes", function () {
layer.set('urlTemplate', 'new_value');
expect(engineMock.reload).toHaveBeenCalled();
});
});
});
+106
View File
@@ -0,0 +1,106 @@
var TooltipTemplate = require('../../../../src/geo/map/tooltip-template');
describe('geo/map/tooltip-template', function () {
describe('.update', function () {
beforeEach(function () {
this.callback = jasmine.createSpy('callback');
this.tooltipTemplate = new TooltipTemplate({
fields: [
{ name: 'Name', title: true, position: 1 }
]
});
this.tooltipTemplate.fields.bind('reset', this.callback, this);
});
it('should reset the fields when new fields are given', function () {
this.tooltipTemplate.update({
fields: [
{ name: 'Name', title: true, position: 1 },
{ name: 'Description', title: false, position: 2 }
]
});
expect(this.tooltipTemplate.fields.toJSON()).toEqual([
{ name: 'Name', title: true, position: 1 },
{ name: 'Description', title: false, position: 2 }
]);
expect(this.callback).toHaveBeenCalled();
});
it('should NOT reset the fields when given the same fields', function () {
this.tooltipTemplate.update({
fields: [
{ name: 'Name', title: true, position: 1 }
]
});
expect(this.tooltipTemplate.fields.toJSON()).toEqual([
{ name: 'Name', title: true, position: 1 }
]);
expect(this.callback).not.toHaveBeenCalled();
});
it('should NOT reset the fields when given the same fields with different `position`', function () {
this.tooltipTemplate.update({
fields: [
{ name: 'Name', title: true, position: 2 }
]
});
expect(this.tooltipTemplate.fields.toJSON()).toEqual([
{ name: 'Name', title: true, position: 1 }
]);
expect(this.callback).not.toHaveBeenCalled();
});
it('should reset the fields when given the same fields with different `position` and order', function () {
this.tooltipTemplate.update({
fields: [
{ name: 'Name', title: true, position: 0 },
{ name: 'Description', title: true, position: 1 }
]
});
expect(this.callback).toHaveBeenCalled();
this.callback.calls.reset();
this.tooltipTemplate.update({
fields: [
{ name: 'Description', title: true, position: 0 },
{ name: 'Name', title: true, position: 1 }
]
});
expect(this.callback).toHaveBeenCalled();
});
it('should clone alternative names', function () {
var callback = jasmine.createSpy('callback');
this.tooltipTemplate.bind('change', callback);
var alternativeNames = {
'name': 'Nombre'
};
this.tooltipTemplate.update({
alternative_names: alternativeNames
});
expect(callback).toHaveBeenCalled();
callback.calls.reset();
// Original object with names is changed
alternativeNames.description = 'Descripción';
this.tooltipTemplate.update({
alternative_names: alternativeNames
});
// Change event has been triggered
expect(callback).toHaveBeenCalled();
});
});
});
+100
View File
@@ -0,0 +1,100 @@
var _ = require('underscore');
var TorqueLayer = require('../../../../src/geo/map/torque-layer');
var sharedTestsForInteractiveLayers = require('./shared-for-interactive-layers');
var createEngine = require('../../fixtures/engine.fixture.js');
describe('geo/map/torque-layer', function () {
var engineMock;
beforeEach(function () {
engineMock = createEngine();
});
sharedTestsForInteractiveLayers(TorqueLayer);
describe('vis reloading', function () {
var ATTRIBUTES = ['sql', 'sql_wrap', 'source'];
_.each(ATTRIBUTES, function (attribute) {
it("should reload the vis when '" + attribute + "' attribute changes", function () {
var layer = new TorqueLayer({}, { engine: engineMock });
layer.set(attribute, 'new_value');
expect(engineMock.reload).toHaveBeenCalled();
});
});
it('should reload the map just once when multiple attributes change', function () {
var layer = new TorqueLayer({}, { engine: engineMock });
var newAttributes = {};
_.each(ATTRIBUTES, function (attr, index) {
newAttributes[attr] = 'new_value_' + index;
});
layer.set(newAttributes);
expect(engineMock.reload).toHaveBeenCalled();
expect(engineMock.reload.calls.count()).toEqual(1);
});
it('should NOT reload the map when cartocss is set and it was previously empty', function () {
var layer = new TorqueLayer({}, { engine: engineMock });
layer.set('cartocss', 'new_value');
expect(engineMock.reload).not.toHaveBeenCalled();
});
it('should NOT reload the map if cartocss property has changed and a reload is not needed', function () {
var layer = new TorqueLayer({
cartocss: 'Map { something: "a"; -torque-time-attribute: "column"; }'
}, { engine: engineMock });
layer.set('cartocss', 'Map { something: "b"; -torque-time-attribute: "column"; }');
expect(engineMock.reload).not.toHaveBeenCalled();
});
_.each([
'-torque-frame-count',
'-torque-time-attribute',
'-torque-aggregation-function',
'-torque-data-aggregation',
'-torque-resolution'
], function (property) {
it('should reload the map if cartocss attribute has changed and "' + property + '"" property has changed', function () {
var layer = new TorqueLayer({
cartocss: 'Map { something: "a"; ' + property + ': "valueA"; }'
}, { engine: engineMock });
layer.set('cartocss', 'Map { something: "b"; ' + property + ': "valueB"; }');
expect(engineMock.reload).toHaveBeenCalled();
});
});
});
describe('.getAnimationDuration', function () {
var layer;
beforeEach(function () {
layer = new TorqueLayer({}, { engine: engineMock });
});
it('should take the animation duration if it is defined', function () {
var cartocss = 'Map { -torque-animation-duration: 22; }';
expect(layer.getAnimationDuration(cartocss)).toBe(22);
cartocss = 'Map { -torque-animation-duration:15; }';
expect(layer.getAnimationDuration(cartocss)).toBe(15);
});
it('should take the defualt animation duration if the attribute isn\'t defined', function () {
var cartocss = 'Map { -torque-animation-aggregation: cartodb_id; }';
expect(layer.getAnimationDuration(cartocss)).toBe(30);
cartocss = 'Map { something: "a"; -torque-time-attribute: "column"; }';
expect(layer.getAnimationDuration(cartocss)).toBe(30);
});
});
});
@@ -0,0 +1,254 @@
var CartoDBLayer = require('../../../src/geo/map/cartodb-layer.js');
var LayersCollection = require('../../../src/geo/map/layers.js');
var CartoDBLayerGroup = require('../../../src/geo/cartodb-layer-group.js');
module.exports = function (createNativeMap, CartoLayerGroupViewClass, getTileUrl, event) {
var nativeMap;
var container;
var cartoDbLayer0;
var cartoDbLayer1;
var layerGroupView;
var layerGroupModelMock;
var layersCollection;
var engineMock = {
on: jasmine.createSpy('on')
};
var mapModelMock = {
on: jasmine.createSpy('on'),
isFeatureInteractivityEnabled: jasmine.createSpy('isFeatureInteractivityEnabled').and.returnValue(false)
};
beforeEach(function () {
container = document.createElement('div');
nativeMap = createNativeMap(container);
cartoDbLayer0 = new CartoDBLayer({}, { engine: engineMock });
cartoDbLayer1 = new CartoDBLayer({}, { engine: engineMock });
layersCollection = new LayersCollection([cartoDbLayer0, cartoDbLayer1]);
layerGroupModelMock = new CartoDBLayerGroup({
urls: {
'subdomains': [0, 1, 2, 3],
'tiles': 'http://{s}.ashbu.cartocdn.com/documentation/api/v1/map/0123456789/{layerIndexes}/{z}/{x}/{y}.png',
'grids': [
[
'http://0.ashbu.cartocdn.com/documentation/api/v1/map/0123456789/0/{z}/{x}/{y}.grid.json',
'http://1.ashbu.cartocdn.com/documentation/api/v1/map/0123456789/0/{z}/{x}/{y}.grid.json',
'http://2.ashbu.cartocdn.com/documentation/api/v1/map/0123456789/0/{z}/{x}/{y}.grid.json',
'http://3.ashbu.cartocdn.com/documentation/api/v1/map/0123456789/0/{z}/{x}/{y}.grid.json'
],
[
'http://0.ashbu.cartocdn.com/documentation/api/v1/map/0123456789/1/{z}/{x}/{y}.grid.json',
'http://1.ashbu.cartocdn.com/documentation/api/v1/map/0123456789/1/{z}/{x}/{y}.grid.json',
'http://2.ashbu.cartocdn.com/documentation/api/v1/map/0123456789/1/{z}/{x}/{y}.grid.json',
'http://3.ashbu.cartocdn.com/documentation/api/v1/map/0123456789/1/{z}/{x}/{y}.grid.json'
]
],
'attributes': [
'http://ashbu.cartocdn.com/documentation/api/v1/map/0123456789/0/attributes',
'http://ashbu.cartocdn.com/documentation/api/v1/map/0123456789/1/attributes'
]
},
indexOfLayersInWindshaft: [1, 2]
}, {
layersCollection: layersCollection
});
});
afterEach(function () {
document.body.removeChild(container);
});
it('should set the right tile url', function () {
layerGroupView = new CartoLayerGroupViewClass(layerGroupModelMock, {
nativeMap: nativeMap,
mapModel: mapModelMock
});
var actual = getTileUrl(layerGroupView);
var expected = 'http://0.ashbu.cartocdn.com/documentation/api/v1/map/0123456789/1,2/{z}/{x}/{y}.png';
expect(actual).toEqual(expected);
});
describe('interactivity', function () {
it('should not enable interaction when there are no interactive layers ', function () {
layerGroupView = new CartoLayerGroupViewClass(layerGroupModelMock, {
nativeMap: nativeMap,
mapModel: mapModelMock
});
expect(layerGroupView.interaction.length).toEqual(0);
});
it('should enable interaction when there are no interactive layers but map model has enabled interaction ', function () {
mapModelMock.isFeatureInteractivityEnabled.and.returnValue(true);
layerGroupView = new CartoLayerGroupViewClass(layerGroupModelMock, {
nativeMap: nativeMap,
mapModel: mapModelMock
});
expect(layerGroupView.interaction.length).toEqual(2); // 2 interactive layers
});
it('should enable interaction when there are interactive layers ', function () {
mapModelMock.isFeatureInteractivityEnabled.and.returnValue(false);
spyOn(cartoDbLayer0, 'isInteractive').and.returnValue(true);
layerGroupView = new CartoLayerGroupViewClass(layerGroupModelMock, {
nativeMap: nativeMap,
mapModel: mapModelMock
});
expect(layerGroupView.interaction.length).toEqual(1);
});
it('should disable interaction for the hidden layers', function () {
mapModelMock.isFeatureInteractivityEnabled.and.returnValue(false);
spyOn(cartoDbLayer0, 'isInteractive').and.returnValue(true);
spyOn(cartoDbLayer1, 'isInteractive').and.returnValue(true);
spyOn(cartoDbLayer1, 'isVisible').and.returnValue(false);
layerGroupView = new CartoLayerGroupViewClass(layerGroupModelMock, {
nativeMap: nativeMap,
mapModel: mapModelMock
});
expect(layerGroupView.interaction.length).toEqual(1);
});
});
describe('event firing', function () {
beforeEach(function () {
spyOn(cartoDbLayer0, 'isInteractive').and.returnValue(true);
layerGroupView = new CartoLayerGroupViewClass(layerGroupModelMock, {
nativeMap: nativeMap,
mapModel: mapModelMock
});
});
it('should trigger a "featureOver" event when interactivity class triggers a mousemove event with some data', function (done) {
event.e.type = 'mousemove';
layerGroupView.on('featureOver', function (event) {
expect(event.layer).toEqual(cartoDbLayer0);
expect(event.layerIndex).toEqual(0);
// Skip this test since the container point is computed with a wrong window offset.
// expect(event.position).toEqual({ x: 264, y: 309 });
// expect(event.latlng).toEqual([42.293564192170095, -8.173828125000002]);
expect(event.feature.name).toEqual('fakeCityName');
expect(event.feature.cartodb_id).toEqual(123);
done();
});
layerGroupView.interaction[0]._eventEmitter.dispatchEvent('mousemove', event);
});
it('should trigger a "featureClick" event when interactivity class triggers a "click" event', function (done) {
event.e.type = 'click';
layerGroupView.on('featureClick', function (event) {
expect(event.layer).toEqual(cartoDbLayer0);
expect(event.layerIndex).toEqual(0);
// Skip this test since the container point is computed with a wrong window offset.
// expect(event.position).toEqual({ x: 264, y: 309 });
// expect(event.latlng).toEqual([42.293564192170095, -8.173828125000002]);
expect(event.feature.name).toEqual('fakeCityName');
expect(event.feature.cartodb_id).toEqual(123);
done();
});
layerGroupView.interaction[0]._eventEmitter.dispatchEvent('click', event);
});
it('should trigger a "featureOut" event when interactivity triggers a featureOut event', function (done) {
event.e.type = 'mousemove';
layerGroupView.on('featureOut', function (event) {
expect(event.layer).toEqual(cartoDbLayer0);
expect(event.layerIndex).toEqual(0);
// Skip this test since the container point is computed with a wrong window offset.
// expect(event.position).toEqual({ x: 264, y: 309 });
// expect(event.latlng).toEqual([42.293564192170095, -8.173828125000002]);
done();
});
layerGroupView.interaction[0]._eventEmitter.dispatchEvent('featureout', event);
});
it('should trigger a "featureError" event when interactivity triggers a featureOut event', function (done) {
pending('Not implemented yet');
layerGroupView.on('featureError', function (event) {
expect(event.layer).toEqual(cartoDbLayer0);
expect(event.layerIndex).toEqual(0);
// Skip this test since the container point is computed with a wrong window offset.
// expect(event.position).toEqual({ x: 264, y: 309 });
// expect(event.latlng).toEqual([42.293564192170095, -8.173828125000002]);
done();
});
layerGroupView.interaction[0]._eventEmitter.dispatchEvent('error', event);
});
});
describe('when new urls are set', function () {
it('should set the URL template', function () {
layerGroupModelMock.set('urls', {
'subdomains': [0, 1, 2, 3],
'tiles': 'http://{s}.ashbu.cartocdn.com/documentation/api/v1/map/9876543210/{layerIndexes}/{z}/{x}/{y}.png',
'grids': [
[
'http://0.ashbu.cartocdn.com/documentation/api/v1/map/9876543210/0/{z}/{x}/{y}.grid.json',
'http://1.ashbu.cartocdn.com/documentation/api/v1/map/9876543210/0/{z}/{x}/{y}.grid.json',
'http://2.ashbu.cartocdn.com/documentation/api/v1/map/9876543210/0/{z}/{x}/{y}.grid.json',
'http://3.ashbu.cartocdn.com/documentation/api/v1/map/9876543210/0/{z}/{x}/{y}.grid.json'
],
[
'http://0.ashbu.cartocdn.com/documentation/api/v1/map/9876543210/1/{z}/{x}/{y}.grid.json',
'http://1.ashbu.cartocdn.com/documentation/api/v1/map/9876543210/1/{z}/{x}/{y}.grid.json',
'http://2.ashbu.cartocdn.com/documentation/api/v1/map/9876543210/1/{z}/{x}/{y}.grid.json',
'http://3.ashbu.cartocdn.com/documentation/api/v1/map/9876543210/1/{z}/{x}/{y}.grid.json'
]
],
'attributes': [
'http://ashbu.cartocdn.com/documentation/api/v1/map/9876543210/0/attributes',
'http://ashbu.cartocdn.com/documentation/api/v1/map/9876543210/1/attributes'
]
});
layerGroupView = new CartoLayerGroupViewClass(layerGroupModelMock, {
nativeMap: nativeMap,
mapModel: mapModelMock
});
var actual = getTileUrl(layerGroupView);
var expected = 'http://0.ashbu.cartocdn.com/documentation/api/v1/map/9876543210/1,2/{z}/{x}/{y}.png';
expect(actual).toEqual(expected);
});
});
describe("when there aren't tile URLS", function () {
it('should fetch empty tiles', function () {
layerGroupModelMock.set('urls', {
'tiles': '',
'grids': [],
'attributes': [
'http://ashbu.cartocdn.com/documentation/api/v1/map/9876543210/0/attributes'
]
});
layerGroupView = new CartoLayerGroupViewClass(layerGroupModelMock, {
nativeMap: nativeMap,
mapModel: mapModelMock
});
var actual = getTileUrl(layerGroupView);
var expected = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
expect(actual).toEqual(expected);
});
});
};
@@ -0,0 +1,169 @@
/**
* Shared behavior for all torque layer view.
*
* Should be called in the context of a torque-layer spec, e.g.:
* SharedTestsForTorqueLayer.call(this);
*
* Expects this.view to be present as an instance of the torque layer view,
* and should be re-created between each test run (i.e. create it in the beforeEach closure).
*/
module.exports = function () {
describe('(shared tests for a torque layer view)', function () {
beforeEach(function () {
this.nativeTorqueLayer = this.view.nativeTorqueLayer;
});
it('should setup initial values on model', function () {
expect(this.view.model.get('step')).toEqual(0);
expect(this.view.model.get('time')).toBeDefined(); // number of Date? varies between leaflet and gmaps
expect(this.view.model.get('steps')).toEqual(100);
expect(this.view.model.get('isRunning')).toBe(true);
});
describe('when step attr changes on model', function () {
beforeEach(function () {
spyOn(this.nativeTorqueLayer, 'play');
spyOn(this.nativeTorqueLayer, 'pause');
});
it('should pause/play', function () {
this.view.model.set('isRunning', false);
expect(this.nativeTorqueLayer.play).not.toHaveBeenCalled();
expect(this.nativeTorqueLayer.pause).toHaveBeenCalled();
this.view.model.set('isRunning', true);
expect(this.nativeTorqueLayer.play).toHaveBeenCalled();
});
});
describe('when step attr changes on model', function () {
beforeEach(function () {
spyOn(this.nativeTorqueLayer, 'setStep');
this.view.model.set('step', 123);
});
it('should setStep when step attr changes on model', function () {
expect(this.nativeTorqueLayer.setStep).toHaveBeenCalledWith(123);
});
});
describe('when steps attr changes on model', function () {
beforeEach(function () {
spyOn(this.nativeTorqueLayer, 'setSteps');
this.view.model.set('steps', 512);
});
it('should setSteps when steps attr changes on model', function () {
expect(this.nativeTorqueLayer.setSteps).toHaveBeenCalledWith(512);
});
});
describe('when renderRange attr changes on model', function () {
beforeEach(function () {
spyOn(this.nativeTorqueLayer, 'renderRange');
spyOn(this.nativeTorqueLayer, 'resetRenderRange');
});
it('should update renderRange if there are values', function () {
this.view.model.set('renderRange', {start: 0, end: 100});
expect(this.nativeTorqueLayer.renderRange).toHaveBeenCalledWith(0, 100);
expect(this.nativeTorqueLayer.resetRenderRange).not.toHaveBeenCalled();
});
it('should reset renderRange if there are no values set', function () {
this.view.model.set('renderRange', null);
expect(this.nativeTorqueLayer.resetRenderRange).toHaveBeenCalled();
this.nativeTorqueLayer.resetRenderRange.calls.reset();
this.view.model.set('renderRange', {});
expect(this.nativeTorqueLayer.resetRenderRange).toHaveBeenCalled();
});
});
describe('when change:time is fired', function () {
beforeEach(function () {
spyOn(this.nativeTorqueLayer, 'setStep');
spyOn(this.nativeTorqueLayer, 'renderRange');
this.nativeTorqueLayer.fire('change:time', {
step: 1,
time: 9000,
start: 0,
end: 100
});
});
it('should update model', function () {
expect(this.view.model.get('step')).toEqual(1);
expect(this.view.model.get('time')).toEqual(9000);
expect(this.view.model.get('renderRange')).toEqual({start: 0, end: 100});
});
it('should not call view methods again', function () {
expect(this.nativeTorqueLayer.setStep).not.toHaveBeenCalled();
expect(this.nativeTorqueLayer.renderRange).not.toHaveBeenCalled();
});
});
describe('when change:steps is fired', function () {
beforeEach(function () {
spyOn(this.nativeTorqueLayer, 'setSteps');
this.nativeTorqueLayer.fire('change:steps', {steps: 1234});
});
it('should update model', function () {
expect(this.view.model.get('steps')).toEqual(1234);
});
it('should not call view methods again', function () {
expect(this.nativeTorqueLayer.setSteps).not.toHaveBeenCalled();
});
});
describe('when play/pause is triggered', function () {
beforeEach(function () {
spyOn(this.view.model, 'pause').and.callThrough();
spyOn(this.view.model, 'play').and.callThrough();
spyOn(this.nativeTorqueLayer, 'pause').and.callThrough();
spyOn(this.nativeTorqueLayer, 'play').and.callThrough();
});
it('should call model play/pause when triggered on view', function () {
this.nativeTorqueLayer.pause();
expect(this.view.model.pause).toHaveBeenCalled();
expect(this.nativeTorqueLayer.pause.calls.count()).toEqual(1); // verify that view is not called again
this.nativeTorqueLayer.play();
expect(this.view.model.play).toHaveBeenCalled();
expect(this.nativeTorqueLayer.play.calls.count()).toEqual(1); // verify that view is not called again
});
});
});
describe('when cartocss attr changes on model', function () {
beforeEach(function () {
this.nativeTorqueLayer = this.view.nativeTorqueLayer;
spyOn(this.nativeTorqueLayer, 'setCartoCSS');
});
it('should set the new cartoCSS on the torque layer', function () {
this.view.model.set('cartocss', '#layer { marker-fill: whatever; }');
expect(this.nativeTorqueLayer.setCartoCSS).toHaveBeenCalledWith('#layer { marker-fill: whatever; }');
});
});
describe('when tileURLTemplates and subdomains attrs change on model', function () {
beforeEach(function () {
this.nativeTorqueLayer = this.view.nativeTorqueLayer;
this.nativeTorqueLayer.provider = {
subdomains: [ '0', '1', '2' ],
options: {},
_setReady: function () {}
};
});
it('should set the new urlTemplate and subdomains on the torque provider', function () {
this.view.model.set('tileURLTemplates', [ 'http://example.com/{z}/{x}/{y}.torque' ]);
expect(this.nativeTorqueLayer.provider.templateUrl).toEqual('http://example.com/{z}/{x}/{y}.torque');
expect(this.nativeTorqueLayer.provider.subdomains).toEqual([ '0', '1', '2' ]);
});
});
};
@@ -0,0 +1,100 @@
var Backbone = require('backbone');
var TorqueLayer = require('../../../src/geo/map/torque-layer');
var TorqueLayerViewBase = require('../../../src/geo/torque-layer-view-base');
var _ = require('underscore');
var createEngine = require('../fixtures/engine.fixture.js');
describe('geo/torque-layer-base-view', function () {
var engineMock;
beforeEach(function () {
spyOn(TorqueLayerViewBase, '_cartoCSSChanged').and.callThrough();
spyOn(TorqueLayerViewBase, '_onUpdateDuration').and.callThrough();
engineMock = createEngine();
this.model = new TorqueLayer({
type: 'torque',
table_name: 'table',
sql_wrap: 'select * from (<%= sql %>) as _cdbfromsqlwrap',
query_wrapper: 'select * from (<%= sql %>) as _cdbfromquerywrapper',
cartocss: 'Map {}',
dynamic_cdn: 'dynamic-cdn-value'
}, { engine: engineMock });
});
describe('_getQuery', function () {
describe('without source', function () {
it('should take sql_wrap in case it is defined', function () {
var query = TorqueLayerViewBase._getQuery(this.model);
expect(query).toBe('select * from (select * from table) as _cdbfromsqlwrap');
});
it('should not take query_wrapper in case sql_wrap is not defined', function () {
this.model.unset('sql_wrap');
var query = TorqueLayerViewBase._getQuery(this.model);
expect(query).toBe('select * from table');
});
});
describe('with source', function () {
var sourceMock = new Backbone.Model({
query: 'select * from new_table'
});
beforeEach(function () {
this.model.set('source', sourceMock);
});
it('should take sql_wrap in case it is defined', function () {
var query = TorqueLayerViewBase._getQuery(this.model);
expect(query).toBe('select * from (select * from new_table) as _cdbfromsqlwrap');
});
it('should not take query_wrapper in case sql_wrap is not defined', function () {
this.model.unset('sql_wrap');
var query = TorqueLayerViewBase._getQuery(this.model);
expect(query).toBe('select * from new_table');
});
afterEach(function () {
this.model.unset('source');
});
});
});
describe('_initBinds', function () {
var test;
beforeEach(function () {
var FakeObj = function () {};
FakeObj.prototype = _.extend(
{
stopListening: function () {}
},
Backbone.Events,
TorqueLayerViewBase
);
test = new FakeObj();
test.model = this.model;
test.nativeTorqueLayer = {
setCartoCSS: function () {},
animator: {
duration: function () {}
}
};
spyOn(test.nativeTorqueLayer.animator, 'duration');
test._onModel();
});
it('should apply cartocss to native torque layer', function () {
this.model.set('cartocss', 'Map { -torque-animation-duration: 30 }');
expect(TorqueLayerViewBase._cartoCSSChanged).toHaveBeenCalled();
});
it('should change animation durator when customDuration changes', function () {
this.model.set('customDuration', 10);
expect(TorqueLayerViewBase._onUpdateDuration).toHaveBeenCalled();
expect(test.nativeTorqueLayer.animator.duration).toHaveBeenCalledWith(10);
});
});
});
+121
View File
@@ -0,0 +1,121 @@
var $ = require('jquery');
var _ = require('underscore');
var AttributionView = require('../../../../src/geo/ui/attribution/attribution-view');
var Map = require('../../../../src/geo/map');
describe('geo/ui/attribution', function () {
beforeEach(function () {
// Disable defer
spyOn(_, 'delay').and.callFake(function (func) { return function () { func.apply(this, arguments); }; });
this.keyEsc = function () {
var e = $.Event('keydown');
e.keyCode = 27; // ESC
$(document).trigger(e);
};
this.map = new Map(null, {
layersFactory: {}
});
this.map.setMapViewSize({ x: 650, y: 1000 });
spyOn(this.map, 'bind').and.callThrough();
spyOn(AttributionView.prototype, 'render').and.callThrough();
this.view = new AttributionView({
map: this.map
});
this.view.render();
this.$button = this.view.$('.js-button');
this.$text = this.view.$('.js-text');
this.viewHasClass = function (className) {
return this.view.$el.hasClass(className);
};
});
describe('render', function () {
it('should render properly', function () {
expect(this.$button.length).toBe(1);
expect(this.$text.length).toBe(1);
});
it('should render expanded if map container is greater than 650px', function () {
this.map.setMapViewSize({ x: 750, y: 1000 });
this.view.render();
expect(this.viewHasClass('CDB-Attribution')).toBeTruthy();
expect(this.viewHasClass('is-active')).toBeTruthy();
});
it('should render collapsed if map container is smaller than 650px', function () {
this.map.setMapViewSize({ x: 450, y: 1000 });
this.view.render();
expect(this.viewHasClass('CDB-Attribution')).toBeTruthy();
expect(this.viewHasClass('is-active')).toBeFalsy();
});
it('should add GMaps properly when provider is not Leaflet', function () {
expect(this.viewHasClass('CDB-Attribution--gmaps')).toBeFalsy();
this.map.set('provider', 'gmaps');
this.view.render();
expect(this.viewHasClass('CDB-Attribution--gmaps')).toBeTruthy();
});
});
describe('attributions change', function () {
it('should render when it is visible', function () {
this.$button.click(); // Making it visible
this.map.trigger('change:attribution');
expect(AttributionView.prototype.render).toHaveBeenCalled();
});
it('should render when it is not visible', function () {
this.map.trigger('change:attribution');
expect(AttributionView.prototype.render).toHaveBeenCalled();
});
});
describe('when the attributions are displayed', function () {
beforeEach(function () {
this.$button.click();
});
it('should have is-active class', function () {
expect(this.viewHasClass('is-active')).toBeTruthy();
});
it('should hide attributions text when js-button is clicked', function () {
this.$button.click();
expect(this.viewHasClass('is-active')).toBeFalsy();
});
it('should collapse the attributions when ESC is pressed', function () {
this.keyEsc();
expect(this.viewHasClass('is-active')).toBeFalsy();
});
});
describe('when attributions are hidden', function () {
beforeEach(function () {
this.$button.click();
this.keyEsc();
});
it('shouldn\'t have is-active class', function () {
expect(this.viewHasClass('is-active')).toBeFalsy();
});
it('should show attributions text when js-button is clicked', function () {
this.$button.click();
expect(this.viewHasClass('is-active')).toBeTruthy();
});
it('should not respond to ESC', function () {
this.keyEsc();
expect(this.viewHasClass('is-active')).toBeFalsy();
});
it('should not respond to clicks on the document', function () {
$(document).trigger('click');
expect(this.viewHasClass('is-active')).toBeFalsy();
});
});
});
+173
View File
@@ -0,0 +1,173 @@
var InfowindowModel = require('../../../../src/geo/ui/infowindow-model');
var InfowindowTemplate = require('../../../../src/geo/map/infowindow-template');
describe('geo/ui/infowindow-model', function () {
it('should set a default template if template is empty', function () {
var infowindowModel = new InfowindowModel({});
expect(infowindowModel.get('template')).toEqual(infowindowModel.DEFAULT_TEMPLATE);
infowindowModel = new InfowindowModel({ template: '' });
expect(infowindowModel.get('template')).toEqual(infowindowModel.DEFAULT_TEMPLATE);
infowindowModel = new InfowindowModel({ template: ' ' });
expect(infowindowModel.get('template')).toEqual(infowindowModel.DEFAULT_TEMPLATE);
});
it('should NOT set a default template if template is present', function () {
var infowindowModel = new InfowindowModel({ template: 'template' });
expect(infowindowModel.get('template')).toEqual('template');
});
describe('.setContent', function () {
it('should NOT include empty fields', function () {
var infowindowModel = new InfowindowModel({ fields: [{ name: 'NAME', title: true }, { name: 'SOMETHING', title: true }] });
infowindowModel.updateContent({ NAME: 'CartoDB' }, { showEmptyFields: false });
expect(infowindowModel.get('content').fields).toEqual([{ name: 'NAME', title: 'NAME', value: 'CartoDB', index: 0 }]);
});
it('should include empty fields', function () {
var infowindowModel = new InfowindowModel({ fields: [{ name: 'NAME', title: true }, { name: 'SOMETHING', title: true }] });
infowindowModel.updateContent({ NAME: 'CartoDB' }, { showEmptyFields: true });
expect(infowindowModel.get('content').fields).toEqual([{ name: 'NAME', title: 'NAME', value: 'CartoDB', index: 0 }, { name: 'SOMETHING', title: 'SOMETHING', value: null, index: 1 }]);
});
});
describe('.contentForFields', function () {
it('should return the title and value of each field', function () {
var attributes = { field1: 'value1' };
var fields = [{ name: 'field1', title: true }];
var content = InfowindowModel.contentForFields(attributes, fields, {});
expect(content.fields.length).toEqual(1);
expect(content.fields[0].title).toEqual('field1');
expect(content.fields[0].value).toEqual('value1');
});
it('should not return the title if not specified', function () {
var attributes = { field1: 'value1' };
var fields = [{ name: 'field1' }]; // Field doesn't have a title attribute
var content = InfowindowModel.contentForFields(attributes, fields, {});
expect(content.fields.length).toEqual(1);
expect(content.fields[0].title).toEqual(null);
});
it('should return the index of each field', function () {
var attributes = { field1: 'value1', field2: 'value2' };
var fields = [{ name: 'field1' }, { name: 'field2' }];
var content = InfowindowModel.contentForFields(attributes, fields, {});
expect(content.fields.length).toEqual(2);
expect(content.fields[0].index).toEqual(0);
expect(content.fields[1].index).toEqual(1);
});
it('should return empty fields', function () {
var attributes = { field1: 'value1' };
var fields = [{ name: 'field1' }, { name: 'field2' }];
var options = { showEmptyFields: true };
var content = InfowindowModel.contentForFields(attributes, fields, options);
expect(content.fields.length).toEqual(2);
expect(content.fields[0]).toEqual({
name: 'field1',
title: null,
value: 'value1',
index: 0
});
expect(content.fields[1]).toEqual({
name: 'field2',
title: null,
value: null,
index: 1
});
});
it('should not return empty fields', function () {
var attributes = { field1: 'value1' };
var fields = [{ name: 'field1' }, { name: 'field2' }];
var options = { showEmptyFields: false };
var content = InfowindowModel.contentForFields(attributes, fields, options);
expect(content.fields.length).toEqual(1);
expect(content.fields[0]).toEqual({
name: 'field1',
title: null,
value: 'value1',
index: 0
});
});
it('should not return fields with a null value', function () {
var attributes = { field1: 'wadus', field2: null };
var fields = [{ name: 'field1' }, { name: 'field2' }];
var content = InfowindowModel.contentForFields(attributes, fields, {});
expect(content.fields.length).toEqual(1);
expect(content.fields[0]).toEqual({
name: 'field1',
title: null,
value: 'wadus',
index: 0
});
});
it('should return the attributes as data', function () {
var attributes = { field1: 'value1' };
var fields = [{ name: 'field1' }];
var content = InfowindowModel.contentForFields(attributes, fields, {});
expect(content.data).toEqual(attributes);
});
it('should return an empty field when no data is available', function () {
var attributes = {};
var fields = [{ name: 'field1' }, { name: 'field2' }];
var content = InfowindowModel.contentForFields(attributes, fields, {});
expect(content.fields.length).toEqual(1);
expect(content.fields[0]).toEqual({
title: null,
value: 'No data available',
index: 0,
type: 'empty'
});
});
});
describe('.setInfowindowTemplate', function () {
it('should only pick and set specific attributes from the given template', function () {
var infowindowModel = new InfowindowModel({ template: 'template' });
infowindowModel.setInfowindowTemplate(new InfowindowTemplate({
template: 'new_template',
something: 'something'
}));
expect(infowindowModel.get('template')).toEqual('new_template');
expect(infowindowModel.get('something')).toBeUndefined();
});
it('should reject attributes from the template that have a falsy value', function () {
var infowindowModel = new InfowindowModel({ template: 'template' });
infowindowModel.setInfowindowTemplate(new InfowindowTemplate({
template: ''
}));
// Attributes have NOT changed
expect(infowindowModel.get('template')).toEqual('template');
infowindowModel.setInfowindowTemplate(new InfowindowTemplate({
template: 'new_template'
}));
// Atributtes have changed
expect(infowindowModel.get('template')).toEqual('new_template');
});
});
});
+603
View File
@@ -0,0 +1,603 @@
var $ = require('jquery');
var Backbone = require('backbone');
var Map = require('../../../../src/geo/map');
var MapView = require('../../../../src/geo/map-view');
var InfowindowModel = require('../../../../src/geo/ui/infowindow-model');
var Infowindow = require('../../../../src/geo/ui/infowindow-view');
describe('geo/ui/infowindow-view', function () {
var model, view, mapView, map;
beforeEach(function () {
var container = $('<div>').css('height', '200px');
map = new Map(null, { layersFactory: {} });
mapView = new MapView({
el: container,
mapModel: map,
engine: new Backbone.Model(),
layerViewFactory: jasmine.createSpyObj('layerViewFactory', ['createLayerView']),
layerGroupModel: new Backbone.Model()
});
model = new InfowindowModel({
fields: [
{ name: 'test1', position: 1, title: true },
{ name: 'test2', position: 2, title: true }
]
});
view = new Infowindow({
model: model,
mapView: mapView
});
});
it('should add render when template changes', function () {
spyOn(view, 'render');
model.set('template', 'jaja');
expect(view.render).toHaveBeenCalled();
});
it('should change width of the infowindow when width attribute changes', function () {
spyOn(view, 'render');
view.model.set({
'template': '<div class="js-infowindow"></div>',
'width': 100
});
expect(view.$('.js-infowindow').css('width')).toBe('100px');
});
it('shouldn\'t change width of the infowindow when width attribute is undefined', function () {
spyOn(view, 'render');
view.model.set({
'template': '<div class="js-infowindow"></div>'
});
var previousWidth = view.$('.js-infowindow').css('width');
// Unset the width from the model
view.model.unset('width');
// Width hasn't changed
expect(view.$('.js-infowindow').css('width')).toBe(previousWidth);
});
it('should change maxHeight of the infowindow when maxHeight attribute changes', function () {
spyOn(view, 'render');
view.model.set({
template: '<div class="js-infowindow"><div class="js-content"></div></div>',
maxHeight: 100
});
expect(view.$('.js-content').css('max-height')).toBe('100px');
});
it('should render without alternative_name set', function () {
var template = '<div class="js-infowindow">' +
'<a href="#close" class="cartodb-popup-close-button close">x</a>' +
'<div class="cartodb-popup-content-wrapper">' +
'<div class="js-content">' +
'<ul id="mylist"></ul>' +
'</div>' +
'</div>' +
'<div class="hook"></div>' +
'</div>';
model.unset('alternative_names');
model.set({
content: {
fields: [{
title: 'test',
value: true,
position: 0,
index: 0
}]
},
template: template
});
expect(view.render().$el.html().length).not.toBe(0);
});
it('should render with alternative_name set', function () {
model.set({
content: {
fields: [
{ name: 'jamon1', title: 'jamon1_', value: 'jamon1' }
]
}
}, { silent: true });
view.render();
var item1 = view.$el.find('.CDB-infowindow-listItem:nth-child(1)');
expect(item1.find('.CDB-infowindow-title').text()).toEqual('jamon1');
expect(item1.find('.CDB-infowindow-subtitle').text()).toEqual('jamon1_');
});
it('should render without title', function () {
model.set({
content: {
fields: [
{ name: 'jamon1', title: null, value: 'jamon1' }
]
}
}, { silent: true });
view.render();
var item1 = view.$el.find('.CDB-infowindow-listItem:nth-child(1)');
expect(item1.find('.CDB-infowindow-title').text()).toEqual('jamon1');
expect(item1.find('.CDB-infowindow-subtitle').length).toBe(0);
});
it('should convert value to string when it is a number', function () {
model.set({
content: {
fields: [{
title: 'jamon1',
value: 0,
index: 0
}, {
title: 'jamon2',
value: 1,
index: 1
}]
}
}, { silent: true });
view.render();
var item1 = view.$el.find('.CDB-infowindow-listItem:nth-child(1)');
expect(item1.find('.CDB-infowindow-title').text()).toEqual('0');
expect(item1.find('.CDB-infowindow-subtitle').text()).toEqual('jamon1');
var item2 = view.$el.find('.CDB-infowindow-listItem:nth-child(2)');
expect(item2.find('.CDB-infowindow-title').text()).toEqual('1');
expect(item2.find('.CDB-infowindow-subtitle').text()).toEqual('jamon2');
});
it('should convert value to \'\' when it is undefined', function () {
model.set({
content: {
fields: [{
title: 'jamon', value: undefined
}]
}
}, { silent: true });
view.render();
var item1 = view.$el.find('.CDB-infowindow-listItem:nth-child(1)');
expect(item1.find('.CDB-infowindow-title').text()).toEqual('');
expect(item1.find('.CDB-infowindow-subtitle').text()).toEqual('jamon');
});
it('should convert value to \'\' when it is null', function () {
model.set('content', {
fields: [{
title: 'jamon',
value: null
}]
}, { silent: true });
view.render();
var item1 = view.$el.find('.CDB-infowindow-listItem:nth-child(1)');
expect(item1.find('.CDB-infowindow-title').text()).toEqual('');
expect(item1.find('.CDB-infowindow-subtitle').text()).toEqual('jamon');
});
it('shouldn\'t convert the value if it is empty', function () {
model.set('content', {
fields: [{
title: 'jamon',
value: ''
}]
}, { silent: true });
view.render();
var item1 = view.$el.find('.CDB-infowindow-listItem:nth-child(1)');
expect(item1.find('.CDB-infowindow-title').text()).toEqual('');
expect(item1.find('.CDB-infowindow-subtitle').text()).toEqual('jamon');
});
it('should leave a string as it is', function () {
model.set('content', {
fields: [{
title: 'jamon',
value: 'jamon is testing'
}]
}, { silent: true });
view.render();
var item1 = view.$el.find('.CDB-infowindow-listItem:nth-child(1)');
expect(item1.find('.CDB-infowindow-title').text()).toEqual('jamon is testing');
expect(item1.find('.CDB-infowindow-subtitle').text()).toEqual('jamon');
});
it('should convert value to string when it is a boolean', function () {
model.set('content', {
fields: [
{ title: 'jamon1', value: false },
{ title: 'jamon2', value: true }
]
}, { silent: true });
view.render();
var item1 = view.$el.find('.CDB-infowindow-listItem:nth-child(1)');
expect(item1.find('.CDB-infowindow-title').text()).toEqual('false');
expect(item1.find('.CDB-infowindow-subtitle').text()).toEqual('jamon1');
var item2 = view.$el.find('.CDB-infowindow-listItem:nth-child(2)');
expect(item2.find('.CDB-infowindow-title').text()).toEqual('true');
expect(item2.find('.CDB-infowindow-subtitle').text()).toEqual('jamon2');
});
it('shouldn\'t convert the value if it is an object or an array', function () {
var template = '<div class="js-infowindow">' +
'<a href="#close" class="cartodb-popup-close-button close">x</a>' +
'<div class="cartodb-popup-content-wrapper">' +
'<div class="js-content">' +
'<ul class="CDB-infowindow-listItem">{{#jamon1}}<li>{{jamon2}}, {{istrue}}, "{{isempty}}", {{isnum}}</li>{{/jamon1}}</ul>' +
'<ul class="CDB-infowindow-listItem">{{#jamon3}}<li>{{.}}</li>{{/jamon3}}</ul>' +
'</div>' +
'</div>' +
'<div class="hook"></div>' +
'</div>';
model.set({
content: {
fields: [
{ name: 'jamon1', title: 'jamon1', value: [{ jamon2: 'jamon2', istrue: true, isempty: '', isnum: 9 }] },
{ name: 'jamon3', title: 'jamon3', value: ['jamon4', 'jamon5'] }
]
},
template: template,
template_type: 'mustache'
});
view.render();
var item1 = view.$el.find('.CDB-infowindow-listItem:nth-child(1)');
expect(item1.text()).toContain('jamon2, true, "", 9');
var item2 = view.$el.find('.CDB-infowindow-listItem:nth-child(2)');
expect(item2.text()).toContain('jamon4jamon5');
});
it('should close the infowindow when user clicks close button', function () {
model.set({
template: '<div><button class="js-close">X</button></div>'
});
view = new Infowindow({
model: model,
mapView: mapView
});
view.render();
// Infowindow is visible
model.set('visibility', true, { silent: true });
expect(model.get('visibility')).toBe(true);
view.$('.js-close').click();
// Infowindow has been closed
expect(model.get('visibility')).toBe(false);
});
describe('custom template', function () {
var model, view;
beforeEach(function () {
var container = $('<div>').css('height', '200px');
map = new Map(null, { layersFactory: {} });
mapView = new MapView({
el: container,
mapModel: map,
engine: new Backbone.Model(),
layerViewFactory: jasmine.createSpyObj('layerViewFactory', ['createLayerView']),
layerGroupModel: new Backbone.Model()
});
model = new InfowindowModel({
template: '<div>{{ test1 }}</div>',
fields: [
{ title: 'test1', position: 1, value: 'x' },
{ title: 'test2', position: 2, value: 'b' }
]
});
view = new Infowindow({
model: model,
mapView: mapView
});
});
it('should compile the template when changes', function () {
view.model.set('template', '<div>{{test1}}</div>');
expect(view.template({ test1: 'new' })).toEqual('<div>new</div>');
});
it('should render properly when there is only a field without title', function () {
view.model.set({
template: ['{{#content.fields}}',
'<li class="CDB-infowindow-listItem">',
'{{#title}}<h5 class="CDB-infowindow-subtitle">{{title}}</h5>{{/title}}',
'{{#value}}<h4 class="CDB-infowindow-title">{{{ value }}}</h4>{{/value}}',
'{{^value}}<h4 class="CDB-infowindow-title">null</h4>{{/value}}',
'</li>',
'{{/content.fields}}'].join(' '),
content: {
fields: [
{ name: 'test1', title: null, position: 0, value: 'jamon' }
]
}
});
view.render();
expect(view.$el.html()).toContain('<li class="CDB-infowindow-listItem"> <h4 class="CDB-infowindow-title">jamon</h4> </li>');
});
it('should render with alternative_name set', function () {
view.model.set({
template: ['{{#content.fields}}',
'<li class="CDB-infowindow-listItem">',
'{{#title}}<h5 class="CDB-infowindow-subtitle">{{title}}</h5>{{/title}}',
'{{#value}}<h4 class="CDB-infowindow-title">{{{ value }}}</h4>{{/value}}',
'{{^value}}<h4 class="CDB-infowindow-title">null</h4>{{/value}}',
'</li>',
'{{/content.fields}}'].join(' '),
content: {
fields: [
{ name: 'test1', title: 'test1_', value: 'test1' }
]
}
});
view.render();
expect(view.$el.html()).toContain('<h5 class="CDB-infowindow-subtitle">test1_</h5> <h4 class="CDB-infowindow-title">test1</h4>');
});
it('shouldn\'t sanitize the fields', function () {
spyOn(view, '_sanitizeField');
view.render();
expect(view._sanitizeField).not.toHaveBeenCalled();
});
it('should sanitize the template output by default', function () {
view.model.set('template', 'no <iframe src="" onload="document.body.appendChild(document.createElement(\'script\')).src=\'http://localhost/xss.js\'"/> no');
view.render();
expect(view.$el.html()).toEqual('no ');
});
it('should allow to override sanitization', function () {
view.model.set({
template: 'no <iframe src="" onload="document.body.appendChild(document.createElement(\'script\')).src=\'http://localhost/xss.js\'"/> no',
sanitizeTemplate: false
});
view.render();
expect(view.$el.html()).toEqual('no <iframe src="" onload="document.body.appendChild(document.createElement(\'script\')).src=\'http://localhost/xss.js\'"></iframe> no');
view.model.set('sanitizeTemplate', null);
view.render();
expect(view.$el.html()).toEqual('no <iframe src="" onload="document.body.appendChild(document.createElement(\'script\')).src=\'http://localhost/xss.js\'"></iframe> no');
var customSanitizeSpy = jasmine.createSpy('sanitizeTemplateSpy').and.returnValue('<p>custom sanitizied result</p>');
view.model.set('sanitizeTemplate', customSanitizeSpy);
expect(customSanitizeSpy).toHaveBeenCalledWith('no <iframe src="" onload="document.body.appendChild(document.createElement(\'script\')).src=\'http://localhost/xss.js\'"/> no');
view.render();
expect(view.$el.html()).toEqual('<p>custom sanitizied result</p>');
view.model.set('sanitizeTemplate', undefined);
view.render();
expect(view.$el.html()).toEqual('no ');
});
});
describe('image template', function () {
var model, view, container, fields, fieldsWithoutURL, fieldsWithInvalidURL, url;
beforeEach(function () {
container = $('<div>').css('height', '200px');
url = 'http://fake.url/image.jpg';
fields = [
{ title: 'test1', position: 1, value: 'http://fake.url/image.jpg' },
{ title: 'test2', position: 2, value: 'b' }
];
fieldsWithInvalidURL = [
{ title: 'test1', position: 1, value: 'invalid_url' },
{ title: 'test2', position: 2, value: 'b' }
];
fieldsWithoutURL = [
{ title: 'test1', position: 1, value: 'x' },
{ title: 'test2', position: 2, value: 'b' }
];
map = new Map(null, { layersFactory: {} });
mapView = new MapView({
el: container,
mapModel: map,
engine: new Backbone.Model(),
layerViewFactory: jasmine.createSpyObj('layerViewFactory', ['createLayerView']),
layerGroupModel: new Backbone.Model()
});
model = new InfowindowModel({
content: {
fields: fields
}
});
view = new Infowindow({
model: model,
mapView: mapView
});
});
it('should get the cover url', function () {
expect(view._getCoverURL()).toEqual(url);
});
it('should validate the cover url', function () {
var url = view._getCoverURL();
expect(view._isValidURL(url)).toEqual(true);
});
it('should accept google chart URLS in the cover', function () {
var url = 'http://chart.googleapis.com/chart?chxl=0:|1990%2F92|2001%2F03|2011%2F13&chxr=1,0,75&chxs=0,676767,11.5,0.5,lt,676767&chxt=x,y&chs=279x210&cht=lc&chco=FF0000&chds=0,69&chd=t:9.5,12.8,15.1,14.6,12.9,10.2,9.5,8.2,7.4,6.2,6,6.5,7.1,7.5,7.9,8,8.2,7.9,7.5,6.9,6.3,5.6&chg=-1,0,0,4&chls=1&chma=0,0,0,25&chm=B,EFEFEF,0,0,0&chtt=Malnourishment+in+&chts=676767,14';
expect(view._isValidURL(url)).toEqual(true);
});
it('shouldn\'t modify the height of the cover when there are several fields', function () {
model.set('content', { fields: fieldsWithoutURL });
model.set('template', '<div class="js-infowindow" data-cover="true"><div class="js-cover" style="height:123px"><div class="CDB-hook"></div></div>');
expect(view._containsCover()).toEqual(true);
expect(view.$('.js-cover').height()).toEqual(123);
expect(view.$('.CDB-hook img').length).toEqual(0);
});
it('should add the image cover class in the custom template', function () {
spyOn(view, '_loadCoverFromTemplate').and.callThrough();
model.set('template', '<div class="js-infowindow" data-cover="true"><div class="js-cover" style="height: 123px"><img src="http://fake.url" style="height: 100px"></div><div class="CDB-hook"></div></div>');
expect(view._loadCoverFromTemplate).toHaveBeenCalled();
expect(view._containsCover()).toEqual(true);
expect(view.$('.CDB-infowindow-media-item').length).toEqual(1);
});
it('should setup the hook correctly', function () {
spyOn(view, '_loadCoverFromUrl').and.callThrough();
model.set('content', { fields: fields });
model.set('template', '<div class="js-infowindow" data-cover="true"><div class="js-cover"></div><div class="CDB-hook"></div></div>');
expect(view._loadCoverFromUrl).toHaveBeenCalled();
view._onLoadImageSuccess();
expect(view.$('.CDB-hook img').length).toEqual(1);
expect(view.$('.CDB-hook img').attr('src')).toEqual('http://fake.url/image.jpg');
});
it('should detect if the infowindow has a cover', function () {
model.set('template', '<div class="js-infowindow" data-cover="true"><div class="js-cover"></div></div>');
expect(view._containsCover()).toEqual(true);
});
it('should render the loader for infowindows with cover', function () {
model.set('content', { fields: fields });
model.set('template', '<div class="js-infowindow" data-cover="true"><div class="js-cover"></div></div>');
expect(view.$('.js-cover .js-loader').length).toEqual(1);
});
it('should append the image', function () {
model.set('template', '<div class="js-infowindow" data-cover="true"><div class="js-cover"></div></div>');
expect(view.$('img').length).toEqual(1);
});
it('shouldn\'t append an image if it\'s not valid', function () {
model.set('content', { fields: fieldsWithoutURL });
model.set('template', '<div class="js-infowindow header" data-cover="true"><div class="js-cover"></div></div>');
expect(view.$('img').length).toEqual(0);
});
it('shouldn\'t append the image if the theme doesn\'t have cover', function () {
model.set('content', { fields: fields });
model.set('template', '<div class="js-cover"></div>');
expect(view.$('img').length).toEqual(0);
});
it('should append a non-valid error message if the image is not valid', function () {
model.set('content', { fields: fieldsWithInvalidURL });
model.set('template', '<div class="js-infowindow header" data-cover="true"><div class="js-cover"></div></div>');
expect(view.$el.find('.CDB-infowindow-fail').length).toEqual(1);
});
it('should add header class', function () {
model.set('template', '<div class="js-infowindow" data-cover="true"><div class="js-header"></div></div>');
expect(view.$el.find('.has-header').length).toEqual(1);
});
it('should add has-header-image class', function () {
model.set('template', '<div class="js-infowindow" data-cover="true"><div class="js-cover"></div></div>');
expect(view.$el.find('.has-header-image').length).toEqual(1);
});
it('should add has-fields class', function () {
model.set('content', { fields: fields });
model.set('template', '<div class="js-infowindow"><div class="js-content"></div></div>');
expect(view.$el.find('.has-fields').length).toEqual(1);
});
it('should load image hook if fields is 2 or less and image height is equal or less than content height', function () {
model.set('template', '<div class="js-infowindow" data-cover="true"><div class="js-cover" style="height: 123px"><img src="http://fake.url" class="CDB-infowindow-media-item"></div><div class="CDB-hook"></div></div>');
spyOn(view, '_loadImageHook').and.callThrough();
view.$el.height(100);
view.$('.CDB-infowindow-media-item').height(200);
view._onLoadImageSuccess();
expect(view._loadImageHook).toHaveBeenCalled();
view.$('.CDB-infowindow-media-item').height(90);
view._loadImageHook.calls.reset();
view._onLoadImageSuccess();
expect(view._loadImageHook).not.toHaveBeenCalled();
view.$('.CDB-infowindow-media-item').height(200);
view._loadImageHook.calls.reset();
fields.push({ title: 'test3', position: 3, value: 'c' });
model.set('fields', fields);
view._onLoadImageSuccess();
expect(view._loadImageHook).not.toHaveBeenCalled();
});
it('should stop the loader and show an error if url is invalid', function () {
spyOn(view, '_stopCoverLoader').and.callThrough();
spyOn(view, '_showInfowindowImageError').and.callThrough();
model.set({
fields: fieldsWithInvalidURL,
template: '<div class="js-infowindow" data-cover="true"><div class="js-cover" style="height: 123px"><img src="invalid.url" style="height: 100px"></div><div class="CDB-hook"></div></div>'
});
view.render();
expect(view._stopCoverLoader).toHaveBeenCalled();
expect(view._showInfowindowImageError).toHaveBeenCalled();
});
it('should stop the loader and show an error if url is invalid', function () {
spyOn(view, '_stopCoverLoader').and.callThrough();
spyOn(view, '_showInfowindowImageError').and.callThrough();
model.set({
fields: fieldsWithInvalidURL,
template: '<div class="js-infowindow" data-cover="true"><div class="js-cover" style="height: 123px"><img src="invalid.url" style="height: 100px"></div><div class="CDB-hook"></div></div>'
});
view.render();
expect(view._stopCoverLoader).toHaveBeenCalled();
expect(view._showInfowindowImageError).toHaveBeenCalled();
});
it('should load the cover from template if it already contains the template', function () {
spyOn(view, '_loadCoverFromTemplate').and.callThrough();
model.set({
fields: fieldsWithInvalidURL,
template: '<div class="js-infowindow" data-cover="true"><div class="js-cover" style="height: 123px"><img src="http://fake.url" style="height: 100px"></div><div class="CDB-hook"></div></div>'
});
view.render();
expect(view._loadCoverFromTemplate).toHaveBeenCalled();
});
it('should load the cover from url if it doesn\'t contain the template', function () {
spyOn(view, '_loadCoverFromUrl').and.callThrough();
model.set({
fields: fieldsWithInvalidURL,
template: '<div class="js-infowindow" data-cover="true"><div class="CDB-hook"></div></div>'
});
view.render();
expect(view._loadCoverFromUrl).toHaveBeenCalled();
});
});
});
@@ -0,0 +1,132 @@
var LegendViewBase = require('../../../../../../src/geo/ui/legends/base/legend-view-base.js');
var LegendModelBase = require('../../../../../../src/geo/map/legends/legend-model-base.js');
var createEngine = require('../../../../fixtures/engine.fixture.js');
var MyLegendModel = LegendModelBase.extend({
TYPE: 'something',
isAvailable: function () {
return true;
}
});
var MyLegendView = LegendViewBase.extend({
_getCompiledTemplate: function () {
return '<h1>It Works!</h1>';
}
});
describe('geo/ui/legends/legend-view-base.js', function () {
var engineMock;
beforeEach(function () {
engineMock = createEngine();
this.model = new MyLegendModel({
title: 'My Beautiful Legend',
preHTMLSnippet: '<p>before</p>',
postHTMLSnippet: '<p>after</p>',
visible: true
}, {
engine: engineMock
});
this.myLegend = new MyLegendView({
model: this.model,
placeholderTemplate: function () {
return '<p>Placeholder</p>';
}
});
});
describe('if model is loading', function () {
beforeEach(function () {
this.model.set('state', 'loading');
this.myLegend.render();
});
it('should mark legend as loading', function () {
expect(this.myLegend.$el.hasClass('is-loading')).toBeTruthy();
});
it('should only render a placeholder', function () {
expect(this.myLegend.$el.html()).toEqual('<p>Placeholder</p>');
});
});
describe('if model has errors', function () {
beforeEach(function () {
this.model.set('state', 'error');
this.myLegend.render();
});
it('should NOT mark legend as loading', function () {
expect(this.myLegend.$el.hasClass('is-loading')).toBeFalsy();
});
it('should render a placeholder', function () {
expect(this.myLegend.$el.html()).toMatch('Placeholder');
});
it('should render an error', function () {
expect(this.myLegend.$el.html()).toMatch('Something went wrong');
});
});
describe('if model has been loaded and has no data', function () {
beforeEach(function () {
this.model.set('state', 'success');
spyOn(this.model, 'isAvailable').and.returnValue(false);
this.myLegend.render();
});
it('should NOT mark legend as loading', function () {
expect(this.myLegend.$el.hasClass('is-loading')).toBeFalsy();
});
it('should render a placeholder', function () {
expect(this.myLegend.$el.html()).toMatch('Placeholder');
});
it('should render a warning', function () {
expect(this.myLegend.$el.html()).toMatch('No data available');
});
});
describe('if model has been loaded and has data', function () {
beforeEach(function () {
this.model.set('state', 'success');
this.myLegend.render();
});
it('should NOT mark legend as loading', function () {
expect(this.myLegend.$el.hasClass('is-loading')).toBeFalsy();
});
it('should NOT render a placeholder', function () {
expect(this.myLegend.$el.html()).not.toMatch('Placeholder');
});
it('should render properly', function () {
var html = this.myLegend.$el.html().split('\n');
expect(html[0]).toMatch('My Beautiful Legend');
expect(html[1]).toEqual('<p>before</p>');
expect(html[2]).toEqual('<h1>It Works!</h1>');
expect(html[3]).toEqual('<p>after</p>');
});
it('should sanitize preHTMLSnippet, compiled template and postHTMLSnippet', function () {
this.model.set({
preHTMLSnippet: '<p>before<script>alert("before");</script></p>',
postHTMLSnippet: '<p>after<script>alert("after");</script></p>'
});
spyOn(this.myLegend, '_getCompiledTemplate').and.returnValue('<h1>It Works!<script>alert("It Works!");</script></h1>');
this.myLegend.render();
var html = this.myLegend.$el.html().split('\n');
expect(html[1]).toEqual('<p>before</p>');
expect(html[2]).toEqual('<h1>It Works!</h1>');
expect(html[3]).toEqual('<p>after</p>');
});
});
});
@@ -0,0 +1,85 @@
var LegendViewBubble = require('../../../../../../src/geo/ui/legends/bubble/legend-view.js');
var BubbleLegendModel = require('../../../../../../src/geo/map/legends/bubble-legend-model.js');
var createEngine = require('../../../../fixtures/engine.fixture.js');
describe('geo/ui/legends/bubbles/legend-view.js', function () {
var engineMock;
beforeEach(function () {
engineMock = createEngine();
this.model = new BubbleLegendModel({
title: 'Bubble',
type: 'bubble',
avg: 30,
fillColor: '#fabada',
postHTMLSnippet: '',
preHTMLSnippet: '',
prefix: '',
suffix: '',
sizes: [2, 4, 8],
values: [5, 10, 20, 40],
visible: true
}, {
engine: engineMock
});
spyOn(this.model, 'isAvailable').and.returnValue(true);
this.legendView = new LegendViewBubble({
model: this.model,
placeholderTemplate: function () {
return '<p>Placeholder</p>';
}
});
});
describe('render', function () {
beforeEach(function () {
this.model.set('state', 'success');
this.legendView.render();
});
it('should render bubbles', function () {
expect(this.legendView.$('.js-bubbleItem').length).toBe(4);
expect(this.legendView._calculateBubbleSizes()).toEqual([100, 50, 25]);
});
it('should place avg properly', function () {
// values: [5, 10, 20, 40];
// range: [0, 25, 50, 100];
this.model.set('avg', 5); // at the beginning of range [0, 25]
expect(this.legendView._calculateAverageSize()).toBe(0);
this.model.set('avg', 7.5); // in the middle of range [0, 25]
expect(this.legendView._calculateAverageSize()).toBe(12.5);
this.model.set('avg', 10); // at the end of range [0, 25]
expect(this.legendView._calculateAverageSize()).toBe(25);
this.model.set('avg', 15); // in the middle of range [25, 50]
expect(this.legendView._calculateAverageSize()).toBe(37.5);
this.model.set('avg', 19); // almost at the top of [25, 50]
expect(this.legendView._calculateAverageSize()).toBe(25 + (50 - 25) * (9 / 10));
this.model.set('avg', 25); // in a quarter of range [50, 100]
expect(this.legendView._calculateAverageSize()).toBe(62.5);
this.model.set('avg', 30); // in the middle of range [50, 100]
expect(this.legendView._calculateAverageSize()).toBe(75); // (50 + (100 - 50) * 0.5)
this.model.set('avg', 35); // in the three quarter of range [50, 100]
expect(this.legendView._calculateAverageSize()).toBe(87.5); // (50 + (100 - 50) * (15 / 20)
this.model.set('avg', 40);
expect(this.legendView._calculateAverageSize()).toBe(100);
});
it('should render custom labels properly', function () {
this.model.set('topLabel', 'foo');
expect(this.legendView.$('.Bubble-numbersItem').length).toBe(1);
expect(this.legendView.$('.Bubble-numbersItem').eq(0).text()).toBe('foo');
expect(this.legendView.$('.Bubble-average').length).toBe(0);
});
});
});
@@ -0,0 +1,67 @@
var CustomLegendViewChoropleth = require('../../../../../../src/geo/ui/legends/custom-choropleth/legend-view.js');
var ChoroplethLegendModel = require('../../../../../../src/geo/map/legends/choropleth-legend-model.js');
var createEngine = require('../../../../fixtures/engine.fixture.js');
describe('geo/ui/legends/custom-choropleth/legend-view.js', function () {
var engineMock;
beforeEach(function () {
engineMock = createEngine();
this.model = new ChoroplethLegendModel({
title: 'Foo',
type: ' choropleth',
postHTMLSnippet: '',
preHTMLSnippet: '',
prefix: '',
suffix: '',
leftLabel: 'low',
rightLabel: 'high',
colors: [
{
label: '1',
value: '#ffc6c4'
}, {
label: '',
value: '#ee919b'
}, {
label: '',
value: '#cc607d'
}, {
label: '',
value: '#9e3963'
}, {
label: '127',
value: '#672044'
}
],
visible: true
}, {
engine: engineMock
});
spyOn(this.model, 'isAvailable').and.returnValue(true);
this.legendView = new CustomLegendViewChoropleth({
model: this.model,
placeholderTemplate: function () {
return '<p>Placeholder</p>';
}
});
});
describe('render', function () {
beforeEach(function () {
this.model.set('state', 'success');
this.legendView.render();
});
it('should render choropleth', function () {
expect(this.legendView.$('.Legend-choropleth').length).toBe(1);
expect(this.legendView.$('h3').text()).toContain('Foo');
expect(this.legendView.$('h3 ~ div p').length).toBe(2);
expect(this.legendView.$('h3 ~ div p').length).toBe(2);
expect(this.legendView.$('h3 ~ div p').eq(0).text()).toContain('low');
expect(this.legendView.$('h3 ~ div p').eq(1).text()).toContain('high');
});
});
});
@@ -0,0 +1,78 @@
var LegendViewChoropleth = require('../../../../../../src/geo/ui/legends/choropleth/legend-view.js');
var ChoroplethLegendModel = require('../../../../../../src/geo/map/legends/choropleth-legend-model.js');
var createEngine = require('../../../../fixtures/engine.fixture.js');
describe('geo/ui/legends/choropleth/legend-view.js', function () {
var engineMock;
beforeEach(function () {
engineMock = createEngine();
this.model = new ChoroplethLegendModel({
title: 'Foo',
type: ' choropleth',
avg: 64,
postHTMLSnippet: '',
preHTMLSnippet: '',
prefix: '',
suffix: '',
colors: [
{
label: '1',
value: '#ffc6c4'
}, {
label: '',
value: '#ee919b'
}, {
label: '',
value: '#cc607d'
}, {
label: '',
value: '#9e3963'
}, {
label: '127',
value: '#672044'
}
],
min: 1,
max: 127,
visible: true
}, {
engine: engineMock
});
spyOn(this.model, 'isAvailable').and.returnValue(true);
this.legendView = new LegendViewChoropleth({
model: this.model,
placeholderTemplate: function () {
return '<p>Placeholder</p>';
}
});
});
describe('render', function () {
beforeEach(function () {
this.model.set('state', 'success');
this.legendView.render();
});
it('should render choropleth', function () {
expect(this.legendView.$('.Legend-choropleth').length).toBe(1);
expect(this.legendView.$('.Legend-choroplethAverage').length).toBe(1);
expect(this.legendView.$('.Legend-choroplethAverage').text()).toContain('64');
expect(this.legendView.$('h3').text()).toContain('Foo');
expect(this.legendView.$('h3 ~ div p').length).toBe(2);
expect(this.legendView.$('h3 ~ div p').length).toBe(2);
expect(this.legendView.$('h3 ~ div p').eq(0).text()).toContain('1');
expect(this.legendView.$('h3 ~ div p').eq(1).text()).toContain('127');
});
it('should hide AVG if custom labels', function () {
this.model.set('leftLabel', 'Bar');
expect(this.legendView.$('.Legend-choroplethAverage').length).toBe(0);
expect(this.legendView.$('h3 ~ div p').eq(0).text()).toContain('Bar');
expect(this.legendView.$('h3 ~ div p').eq(1).text()).toContain('127');
});
});
});
@@ -0,0 +1,131 @@
var $ = require('jquery');
var ImgLoaderView = require('../../../../../../src/geo/ui/legends/base/img-loader-view.js');
describe('geo/ui/legends/custom/img-loader-view', function () {
var svgResponse = {
status: 200,
contentType: 'image/svg+xml',
responseText: '<svg xmlns:a="genius.com" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"><path></path></svg>'
};
beforeEach(function () {
jasmine.Ajax.install();
jasmine.Ajax.stubRequest(new RegExp('^http(s)?.*/logo.svg.*'))
.andReturn(svgResponse);
this.svgUrl = 'http://image.io/logo.svg';
this.color = '#cebada';
var $el = $('<div class="js-image-container" data-icon="' + this.svgUrl + '" data-color="' + this.color + '">');
this.imageClass = 'Legend-fillImageAsset';
this.view = new ImgLoaderView({
el: $el,
imageClass: this.imageClass
});
});
afterEach(function () {
jasmine.Ajax.uninstall();
});
describe('._loadImage', function () {
it('should render resource if format is not SVG', function () {
this.imgUrl = 'http://www.imageserver.com/image.png';
var $pngEl = $('<div class="js-image-container" data-icon="' + this.imgUrl + '" data-color="' + this.color + '">');
this.imageClass = 'Legend-fillImageAsset';
var view = new ImgLoaderView({
el: $pngEl,
imageClass: this.imageClass
});
view._loadImage();
var $img = view.$('img');
var classes = $img.attr('class');
var src = $img.attr('src');
var crossOrigin = $img.attr('crossOrigin');
$img.empty(); // To avoid DOM requests to the image file
expect(classes.indexOf('js-image') > -1).toBe(true);
expect(classes.indexOf(this.imageClass) > -1).toBe(true);
expect(src).toBe(this.imgUrl + '?req=markup');
expect(crossOrigin).toBe('anonymous');
});
it('should render SVG tag if resource is SVG file', function () {
this.view._loadImage();
var $svg = this.view.$('svg');
var classes = $svg.attr('class');
var svgFill = $svg.css('fill').toLowerCase();
var pathFill = $svg.find('path')[0].style['fill'].toLowerCase();
expect($svg.length).toBe(1);
expect(classes.indexOf('js-image') > -1).toBe(true);
expect(classes.indexOf(this.imageClass) > -1).toBe(true);
expect(svgFill === 'rgb(206, 186, 218)' || svgFill === this.color).toBe(true);
expect(pathFill).toBe('inherit');
});
});
describe('._requestImage', function () {
var obj = {
callback: function () {}
};
it('successful caches the response and calls the callback', function () {
var expectedUrl = this.svgUrl + '?req=ajax';
spyOn(obj, 'callback');
this.view._lastImage.url = null;
this.view._lastImage.content = null;
jasmine.Ajax.requests.reset();
this.view._requestImage(this.svgUrl, obj.callback);
expect(jasmine.Ajax.requests.mostRecent().url).toBe(expectedUrl);
expect(obj.callback).toHaveBeenCalled();
expect(this.view._lastImage.url).toBe(expectedUrl);
expect(this.view._lastImage.content).toBeDefined();
});
it('_requestImage with cached response, does not make an Ajax call and it calls the callback', function () {
var expectedUrl = this.svgUrl + '?req=ajax';
spyOn(obj, 'callback');
this.view._requestImage(this.svgUrl, obj.callback);
jasmine.Ajax.requests.reset();
this.view._requestImage(this.svgUrl, obj.callback);
expect(jasmine.Ajax.requests.mostRecent()).toBeUndefined();
expect(obj.callback).toHaveBeenCalled();
expect(this.view._lastImage.url).toBe(expectedUrl);
expect(this.view._lastImage.content).toBeDefined();
});
it('_requestImage with error response, throws an error', function () {
this.view._lastImage.url = null;
this.view._lastImage.content = null;
jasmine.Ajax.stubRequest(new RegExp('^http(s)?.*/logo.svg.*'))
.andReturn({ status: 500 });
var foo = function () {
this.view._requestImage(this.svgUrl, obj.callback);
};
expect(foo).toThrow();
});
});
describe('._isSVG', function () {
it('behaves properly', function () {
expect(this.view._isSVG('http://image.io/image.svg')).toBe(true);
expect(this.view._isSVG('http://image.io/image.svg?req=ajax')).toBe(true);
expect(this.view._isSVG('http://image.io/image.png')).toBe(false);
expect(this.view._isSVG('http://image.io/image.png?req=markup')).toBe(false);
expect(this.view._isSVG('')).toBe(false);
});
});
});
@@ -0,0 +1,65 @@
var LegendViewCustom = require('../../../../../../src/geo/ui/legends/custom/legend-view.js');
var CustomLegendModel = require('../../../../../../src/geo/map/legends/custom-legend-model.js');
var createEngine = require('../../../../fixtures/engine.fixture.js');
describe('geo/ui/legends/custom/legend-view.js', function () {
var engineMock;
beforeEach(function () {
engineMock = createEngine();
this.model = new CustomLegendModel({
title: 'Foo',
type: 'custom',
html: '',
postHTMLSnippet: '',
preHTMLSnippet: '',
items: [
{
title: 'foo',
color: '#ffc6c4'
}, {
title: 'bar',
color: '#ff6600'
}, {
title: 'bar',
color: '#ff6600',
icon: 'https://s3.amazonaws.com/com.cartodb.users-assets.production/maki-icons/circle-stroked-18.svg'
}
],
visible: true
}, {
engine: engineMock
});
spyOn(this.model, 'isAvailable').and.returnValue(true);
this.legendView = new LegendViewCustom({
model: this.model,
placeholderTemplate: function () {
return '<p>Placeholder</p>';
}
});
});
describe('render', function () {
beforeEach(function () {
this.model.set('state', 'success');
this.legendView.render();
});
it('should render custom', function () {
expect(this.legendView.$('li').length).toBe(3);
expect(this.legendView.$('.Legend-categoryCircle').length).toBe(2);
expect(this.legendView.$('.Legend-categoryIcon').length).toBe(1);
});
it('should render custom html', function () {
this.model.set('html', '<ul><li>Foo</li></ul>');
this.legendView.render();
expect(this.legendView.$('li').length).toBe(1);
expect(this.legendView.$('.Legend-categoryCircle').length).toBe(0);
expect(this.legendView.$('li').eq(0).text()).toContain('Foo');
});
});
});
@@ -0,0 +1,109 @@
var $ = require('jquery');
var Backbone = require('backbone');
var LayerLegendsView = require('../../../../../src/geo/ui/legends/layer-legends-view');
var CartoDBLayer = require('../../../../../src/geo/map/cartodb-layer');
describe('geo/ui/legends/layer-legends-view', function () {
beforeEach(function () {
var engineMock = new Backbone.Model();
engineMock.reload = jasmine.createSpy('reload');
this.settingsModel = new Backbone.Model({
showLegends: true,
showLayerSelector: true,
layerSelectorEnabled: true
});
this.cartoDBLayer = new CartoDBLayer({
layer_name: 'CartoDB Layer #1',
legends: [
{ type: 'bubble', title: 'Bubble Legend' },
{ type: 'category', title: 'Category Legend' }
]
}, { engine: engineMock });
this.tryContainerVisibility = jasmine.createSpy('tryContainerVisibility');
this.layerLegendsView = new LayerLegendsView({
model: this.cartoDBLayer,
settingsModel: this.settingsModel,
tryContainerVisibility: this.tryContainerVisibility
});
this.layerLegendsView.render();
$('body').append(this.layerLegendsView.$el);
});
afterEach(function () {
this.layerLegendsView.$el.remove();
});
it('should render all legends', function () {
// Legends of all types have been rendered
expect(this.layerLegendsView.$('.CDB-Legend-item').length).toEqual(6);
});
it('should trigger a "render" event when the view is rendered', function () {
var callback = jasmine.createSpy('callback');
this.layerLegendsView.on('render', callback);
this.layerLegendsView.render();
expect(callback).toHaveBeenCalled();
});
it('should update the name of the layer when it changes', function () {
expect(this.layerLegendsView.$el.html()).toMatch('CartoDB Layer #1');
this.cartoDBLayer.set('layer_name', 'A NEW layer name');
expect(this.layerLegendsView.$el.html()).not.toMatch('CartoDB Layer #1');
expect(this.layerLegendsView.$el.html()).toMatch('A NEW layer name');
});
it('should hide/show the layer if check next to title is clicked', function () {
expect(this.cartoDBLayer.isVisible()).toBeTruthy();
this.layerLegendsView.$('.js-toggle-layer').trigger('click');
expect(this.cartoDBLayer.isVisible()).toBeFalsy();
this.layerLegendsView.$('.js-toggle-layer').trigger('click');
expect(this.cartoDBLayer.isVisible()).toBeTruthy();
});
it('should uncheck/check the check if layer is hidden/shown', function () {
expect(this.layerLegendsView.$('.js-toggle-layer').is(':checked')).toBe(true);
this.cartoDBLayer.hide();
expect(this.layerLegendsView.$('.js-toggle-layer').is(':checked')).toBe(false);
this.cartoDBLayer.show();
expect(this.layerLegendsView.$('.js-toggle-layer').is(':checked')).toBe(true);
});
it('should hide/show legend views when layer is hidden/shown', function () {
this.cartoDBLayer.hide();
expect(this.layerLegendsView.$('.js-legends').length).toBe(0);
this.cartoDBLayer.show();
expect(this.layerLegendsView.$('.js-legends').length).toBe(1);
});
it('should be hidden if no legends are rendered', function () {
spyOn(this.cartoDBLayer.legends, 'hasAnyLegend').and.returnValue(false);
this.settingsModel.set('showLayerSelector', false);
this.layerLegendsView.render();
expect(this.layerLegendsView.$el.is(':empty')).toBe(true);
});
it('should not render layer selector checkbox if not embed', function () {
this.settingsModel.set('layerSelectorEnabled', false);
this.layerLegendsView.render();
expect(this.layerLegendsView.$('.js-toggle-layer').length).toBe(0);
});
});
@@ -0,0 +1,103 @@
var Backbone = require('backbone');
var LegendsView = require('../../../../../src/geo/ui/legends/legends-view');
var TileLayer = require('../../../../../src/geo/map/tile-layer');
var CartoDBLayer = require('../../../../../src/geo/map/cartodb-layer');
var TorqueLayer = require('../../../../../src/geo/map/torque-layer');
var LayersCollection = require('../../../../../src/geo/map/layers');
var MockFactory = require('../../../../helpers/mockFactory');
describe('geo/ui/legends/legends-view', function () {
beforeEach(function () {
var engineMock = new Backbone.Model();
engineMock.reload = jasmine.createSpy('reload');
this.tileLayer = new TileLayer(null, { engine: {} });
this.cartoDBLayer1 = new CartoDBLayer({
source: MockFactory.createAnalysisModel({ id: 'a1' }),
layer_name: 'CartoDB Layer #1',
legends: []
}, { engine: engineMock });
this.cartoDBLayer2 = new CartoDBLayer({
source: MockFactory.createAnalysisModel({ id: 'a2' }),
layer_name: 'CartoDB Layer #2',
legends: []
}, { engine: engineMock });
this.torqueLayer = new TorqueLayer({ layer_name: 'Torque Layer #3', legends: [] }, { engine: engineMock });
this.layersCollection = new LayersCollection([]);
this.layersCollection.reset([this.tileLayer, this.cartoDBLayer1, this.cartoDBLayer2, this.torqueLayer]);
this.settingsModel = new Backbone.Model({
showLegends: true,
showLayerSelector: true,
layerSelectorEnabled: true
});
this.legendsView = new LegendsView({
layersCollection: this.layersCollection,
settingsModel: this.settingsModel
});
this.legendsView.render();
});
var getLayerLegendTitles = function (legendsView) {
var titleElements = legendsView.$('.CDB-LayerLegends > .CDB-Text').get();
return titleElements.map(function (element) { return element.textContent.trim(); });
};
it('should render legends for CartoDB and torque layers', function () {
expect(this.legendsView.$('.CDB-LayerLegends').length).toEqual(3);
expect(getLayerLegendTitles(this.legendsView)).toEqual(['Torque Layer #3', 'CartoDB Layer #2', 'CartoDB Layer #1']);
});
it('should re-render when a layer with legends is removed from the collection of layers', function () {
this.cartoDBLayer1.remove();
expect(this.legendsView.$('.CDB-LayerLegends').length).toEqual(2);
expect(getLayerLegendTitles(this.legendsView)).toEqual(['Torque Layer #3', 'CartoDB Layer #2']);
});
it('should re-render when new layers with legends are added to the collection of layers', function () {
this.cartoDBLayer1.remove();
this.layersCollection.add(this.cartoDBLayer1, { at: 1 });
expect(this.legendsView.$('.CDB-LayerLegends').length).toEqual(3);
expect(getLayerLegendTitles(this.legendsView)).toEqual(['Torque Layer #3', 'CartoDB Layer #2', 'CartoDB Layer #1']);
});
it('should re-render when a layer with legends is moved', function () {
expect(getLayerLegendTitles(this.legendsView)).toEqual(['Torque Layer #3', 'CartoDB Layer #2', 'CartoDB Layer #1']);
this.layersCollection.moveCartoDBLayer(2, 1);
expect(getLayerLegendTitles(this.legendsView)).toEqual(['Torque Layer #3', 'CartoDB Layer #1', 'CartoDB Layer #2']);
});
it('should show legends if showLegends is true', function () {
this.settingsModel.set('showLegends', true);
expect(this.legendsView.$('.Legends').length).toBe(3);
});
it('should hide legends if showLegends is false', function () {
this.settingsModel.set('showLegends', false);
expect(this.legendsView.$('.Legends').length).toBe(0);
});
it('should show layer selector if showLayerSelector is true', function () {
this.settingsModel.set('showLayerSelector', true);
expect(this.legendsView.$('input').length).toBe(3);
});
it('should hide layer selector if showLayerSelector is false', function () {
this.settingsModel.set('showLayerSelector', false);
expect(this.legendsView.$('input').length).toBe(0);
});
it('should hide element if showLayerSelector changes to false and no legends are visible', function () {
spyOn(this.cartoDBLayer1.legends, 'hasAnyLegend').and.returnValue(false);
spyOn(this.cartoDBLayer2.legends, 'hasAnyLegend').and.returnValue(false);
expect(this.legendsView.el.style.display).toEqual('block');
this.settingsModel.set('showLayerSelector', false);
expect(this.legendsView.el.style.display).toEqual('none');
});
});
+95
View File
@@ -0,0 +1,95 @@
var $ = require('jquery');
var _ = require('underscore');
var LimitsView = require('../../../../src/geo/ui/limits/limits-view');
var Map = require('../../../../src/geo/map');
describe('geo/ui/limits', function () {
beforeEach(function () {
// Disable defer
spyOn(_, 'delay').and.callFake(function (func) {
return function () {
func.apply(this, arguments);
};
});
this.keyEsc = function () {
var event = $.Event('keydown');
event.keyCode = 27; // ESC
$(document).trigger(event);
};
this.map = new Map(null, {
layersFactory: {}
});
spyOn(this.map, 'bind').and.callThrough();
spyOn(LimitsView.prototype, 'render').and.callThrough();
this.view = new LimitsView({
map: this.map
});
this.view.render();
this.$button = this.view.$('.js-button');
this.$text = this.view.$('.js-text');
this.viewHasClass = function (className) {
return this.view.$el.hasClass(className);
};
});
describe('render', function () {
it('should render properly', function () {
expect(this.$button.length).toBe(1);
expect(this.$text.length).toBe(1);
});
});
describe('when the limits overlay is displayed', function () {
beforeEach(function () {
this.$button.click();
});
it('should have .is-active class', function () {
expect(this.viewHasClass('is-active')).toBeTruthy();
});
it('should hide the overlay when .js-button is clicked', function () {
this.$button.click();
expect(this.viewHasClass('is-active')).toBeFalsy();
});
it('should hide the overlay when ESC is pressed', function () {
this.keyEsc();
expect(this.viewHasClass('is-active')).toBeFalsy();
});
it('should hide the overlay when user clicks on the document', function () {
$(document).trigger('click');
expect(this.viewHasClass('is-active')).toBeFalsy();
});
});
describe('when the limits overlay is hidden', function () {
beforeEach(function () {
this.$button.click();
this.keyEsc();
});
it('should not have .is-active class', function () {
expect(this.viewHasClass('is-active')).toBeFalsy();
});
it('should show the overlay when js-button is clicked', function () {
this.$button.click();
expect(this.viewHasClass('is-active')).toBeTruthy();
});
it('should ignore the ESC key', function () {
this.keyEsc();
expect(this.viewHasClass('is-active')).toBeFalsy();
});
it('should ignore clicks in the document', function () {
$(document).trigger('click');
expect(this.viewHasClass('is-active')).toBeFalsy();
});
});
});
+217
View File
@@ -0,0 +1,217 @@
var Backbone = require('backbone');
var OverlaysView = require('../../../../src/geo/ui/overlays-view.js');
var Engine = require('../../../../src/engine');
var createEngine = require('../../fixtures/engine.fixture.js');
describe('src/geo/ui/overlays-view.js', function () {
var mapModelMock = new Backbone.Model();
var engineMock;
beforeEach(function () {
engineMock = createEngine();
this.overlaysCollection = new Backbone.Collection([
{
type: 'zoom',
order: 2,
options: {
display: true
}
},
{
type: 'search',
order: 1,
options: {
display: false
}
},
{
type: 'loader',
order: 5,
options: {
display: true
}
}
]);
this.view = new OverlaysView({
overlaysCollection: this.overlaysCollection,
engine: engineMock,
visView: new Backbone.View({
model: new Backbone.Model({ showLimitErrors: false })
}),
mapModel: mapModelMock,
mapView: new Backbone.View()
});
this.view.render();
});
it('should render existing overlays', function () {
expect(this.view.$('.CDB-OverlayContainer').length).toEqual(1);
// Some overlays are grouped together in a container
expect(this.view.$('.CDB-OverlayContainer > .CDB-Zoom').length).toEqual(1);
expect(this.view.$('.CDB-OverlayContainer > .CDB-Search').length).toEqual(1);
// Some others are global
expect(this.view.$('> .CDB-Loader').length).toEqual(1);
});
it('should only show overlays with a truthy display option', function () {
expect(this.view.$('.CDB-OverlayContainer > .CDB-Zoom').length).toEqual(1);
expect(this.view.$('.CDB-OverlayContainer > .CDB-Search').length).toEqual(1);
expect(this.view.$('> .CDB-Loader').length).toEqual(1);
});
it('should re-render overlays when a new overlay is added', function () {
this.overlaysCollection.add({ type: 'fullscreen' });
expect(this.view.$('.CDB-OverlayContainer > .CDB-Zoom').length).toEqual(1);
expect(this.view.$('.CDB-OverlayContainer > .CDB-Search').length).toEqual(1);
expect(this.view.$('.CDB-OverlayContainer > .CDB-Fullscreen').length).toEqual(1);
expect(this.view.$('> .CDB-Loader').length).toEqual(1);
});
it('should re-render overlays when an overlay is removed', function () {
this.overlaysCollection.remove(this.overlaysCollection.at(0));
expect(this.view.$('.CDB-OverlayContainer > .CDB-Zoom').length).toEqual(0);
expect(this.view.$('.CDB-OverlayContainer > .CDB-Search').length).toEqual(1);
expect(this.view.$('> .CDB-Loader').length).toEqual(1);
});
it('should handle unknown overlay types properly', function () {
this.overlaysCollection.add({ type: 'unknown' });
expect(this.view.$('.CDB-OverlayContainer > .CDB-Zoom').length).toEqual(1);
expect(this.view.$('.CDB-OverlayContainer > .CDB-Search').length).toEqual(1);
expect(this.view.$('> .CDB-Loader').length).toEqual(1);
});
it('should toggle the loader overlay', function () {
var loaderOverlay = this.view.$('> .CDB-Loader');
expect(loaderOverlay.hasClass('is-visible')).toBeFalsy();
engineMock._eventEmmitter.trigger(Engine.Events.RELOAD_STARTED);
expect(loaderOverlay.hasClass('is-visible')).toBeTruthy();
engineMock._eventEmmitter.trigger(Engine.Events.RELOAD_SUCCESS);
expect(loaderOverlay.hasClass('is-visible')).toBeFalsy();
});
describe('._initBinds', function () {
it('should call ._addLimitsOverlay on mapModel error:limit', function () {
spyOn(this.view, '_addLimitsOverlay');
this.view._initBinds();
this.view._mapModel.trigger('error:limit');
expect(this.view._addLimitsOverlay).toHaveBeenCalled();
});
it('should call ._addTilesOverlay on mapModel error:limit', function () {
spyOn(this.view, '_addTilesOverlay');
this.view._initBinds();
this.view._mapModel.trigger('error:tile');
expect(this.view._addTilesOverlay).toHaveBeenCalled();
});
});
describe('._areLimitsErrorsEnabled', function () {
it('should return visModel showLimitErrors', function () {
expect(this.view._areLimitsErrorsEnabled()).toBe(false);
this.view._visView.model.set('showLimitErrors', true);
expect(this.view._areLimitsErrorsEnabled()).toBe(true);
});
});
describe('._hasLimitsOverlay', function () {
it('should return true if overlaysCollection has a limits overlay', function () {
expect(this.view._hasLimitsOverlay()).toBe(false);
this.view._overlaysCollection.add({ type: 'limits' });
expect(this.view._hasLimitsOverlay()).toBe(true);
});
});
describe('._addLimitsOverlay', function () {
describe('when visModel showLimitErrors is true', function () {
beforeEach(function () {
this.view._visView.model.set('showLimitErrors', true);
});
it('should call ._removeTilesOverlay', function () {
spyOn(this.view, '_removeTilesOverlay');
this.view._mapModel.trigger('error:limit');
expect(this.view._removeTilesOverlay).toHaveBeenCalled();
});
it('should add the overlay to the collection', function () {
spyOn(this.view._overlaysCollection, 'add');
this.view._mapModel.trigger('error:limit');
expect(this.view._overlaysCollection.add).toHaveBeenCalled();
});
it('should add only one overlay of type limit', function () {
spyOn(this.view._overlaysCollection, 'add').and.callThrough();
this.view._mapModel.trigger('error:limit');
this.view._mapModel.trigger('error:limit');
expect(this.view._overlaysCollection.add.calls.count()).toEqual(1);
});
});
describe('when visModel showLimitErrors is false', function () {
it('should not add the overlay to the collection', function () {
spyOn(this.view._overlaysCollection, 'add');
this.view._mapModel.trigger('error:limit');
expect(this.view._overlaysCollection.add).not.toHaveBeenCalled();
});
});
});
describe('._addTilesOverlay', function () {
it('should add the overlay to the collection', function () {
spyOn(this.view._overlaysCollection, 'add');
this.view._mapModel.trigger('error:tile');
expect(this.view._overlaysCollection.add).toHaveBeenCalled();
});
it('should add only one overlay of type tile', function () {
spyOn(this.view._overlaysCollection, 'add').and.callThrough();
this.view._mapModel.trigger('error:tile');
this.view._mapModel.trigger('error:tile');
expect(this.view._overlaysCollection.add.calls.count()).toEqual(1);
});
it('should not add the overlay if there is a limits overlay', function () {
this.view._overlaysCollection.add({ type: 'limits' });
spyOn(this.view._overlaysCollection, 'add');
this.view._mapModel.trigger('error:tile');
expect(this.view._overlaysCollection.add).not.toHaveBeenCalled();
});
});
describe('._removeLimitsOverlay', function () {
it('should remove the limits overlay from the collection', function () {
spyOn(this.view._overlaysCollection, 'remove').and.callThrough();
expect(this.view._overlaysCollection.length).toEqual(3);
this.view._overlaysCollection.add({ type: 'limits' });
expect(this.view._overlaysCollection.length).toEqual(4);
this.view._removeLimitsOverlay();
expect(this.view._overlaysCollection.remove).toHaveBeenCalled();
expect(this.view._overlaysCollection.length).toEqual(3);
});
});
describe('._removeTilesOverlay', function () {
it('should remove the tiles overlay from the collection', function () {
spyOn(this.view._overlaysCollection, 'remove').and.callThrough();
expect(this.view._overlaysCollection.length).toEqual(3);
this.view._overlaysCollection.add({ type: 'tiles' });
expect(this.view._overlaysCollection.length).toEqual(4);
this.view._removeTilesOverlay();
expect(this.view._overlaysCollection.remove).toHaveBeenCalled();
expect(this.view._overlaysCollection.length).toEqual(3);
});
});
});
+281
View File
@@ -0,0 +1,281 @@
var $ = require('jquery');
var Backbone = require('backbone');
var Search = require('../../../../src/geo/ui/search/search');
var mapboxGeocoder = require('../../../../src/geo/geocoder/mapbox-geocoder');
var tomtomGeocoder = require('../../../../src/geo/geocoder/tomtom-geocoder');
var Map = require('../../../../src/geo/map');
var LeafletMapView = require('../../../../src/geo/leaflet/leaflet-map-view');
var fakeEvent = {
preventDefault: jasmine.createSpy()
};
describe('geo/ui/search', function () {
beforeEach(function () {
this.$el = $('<div>')
.attr('id', 'map')
.height(500)
.width(500);
$('body').append(this.$el);
this.map = new Map(null, { layersFactory: {} });
this.mapView = new LeafletMapView({
el: this.$el,
mapModel: this.map,
engine: new Backbone.Model(),
layerViewFactory: jasmine.createSpyObj('layerViewFactory', ['createLayerView']),
layerGroupModel: new Backbone.Model()
});
this.mapView.render();
this.view = new Search({
model: this.map,
mapView: this.mapView,
token: 'a_valid_api_key'
});
this.view.render();
});
it('should use tomtom geocoder by default', function () {
expect(this.view.geocoder).toBe(tomtomGeocoder);
});
it('should allow changing geocoder easily', function () {
var search = new Search({
model: this.map,
mapView: this.mapView,
geocoderService: 'mapbox', // <<<
token: 'a_valid_mapbox_api_key'
});
expect(search.geocoder).toBe(mapboxGeocoder);
});
it('should render properly', function () {
expect(this.view.$('.js-form').length).toBe(1);
expect(this.view.$('.js-textInput').length).toBe(1);
expect(this.view.$('input[type="submit"]').length).toBe(0);
expect(this.view.$('span.loader').length).toBe(0);
});
describe('onSubmit', function () {
beforeEach(function () {
this.result = {
lat: 43.0,
lon: -3.0,
boundingbox: {
south: 6.0,
north: 4.0,
west: 6.0,
east: 4.0
},
type: undefined
};
// spyOn(this.view.geocoder, 'geocode').and.callThrough();
spyOn(this.view.geocoder, 'geocode').and.returnValue(Promise.resolve([{
center: [40.41889, -3.69194],
type: 'localadmin'
}]));
this.view.$('.js-textInput').val('Madrid, Spain');
});
it('should search with geocoder when form is submit', function () {
this.view.$('.js-form').submit();
expect(this.view.geocoder.geocode).toHaveBeenCalled();
});
it('should change map center when geocoder returns any result', function (done) {
var onCenterChangedSpy = jasmine.createSpy('onCenterChangedSpy');
this.map.bind('change:center', onCenterChangedSpy, this.view);
this.view._onSubmit(fakeEvent).then(function () {
expect(onCenterChangedSpy).toHaveBeenCalled();
this.map.unbind('change:center', onCenterChangedSpy, this.view);
done();
}.bind(this));
});
describe('result zoom', function () {
function testZoom (context, featureType, expectedZoom, done) {
context.view.geocoder.geocode.and.returnValue(Promise.resolve([{
center: [43.0, -3],
type: featureType
}]));
context.view._onSubmit(fakeEvent).then(function () {
expect(context.map.get('zoom')).toBe(expectedZoom);
done();
});
}
it('should zoom to 18 when search result is building type', function (done) {
testZoom(this, 'building', 18, done);
});
it('should zoom to 15 when search result is postal-area type', function (done) {
testZoom(this, 'postal-area', 15, done);
});
it('should zoom to 18 when search result is venue type', function (done) {
testZoom(this, 'venue', 18, done);
});
it('should zoom to 8 when search result is region type', function (done) {
testZoom(this, 'region', 8, done);
});
it('should zoom to 5 when search result is country type', function (done) {
testZoom(this, 'country', 5, done);
});
it('should zoom to 8 when search result is county type', function (done) {
testZoom(this, 'county', 8, done);
});
it('should zoom to 18 when search result is address type', function (done) {
testZoom(this, 'address', 18, done);
});
it('should zoom to 12 when search result is locality type', function (done) {
testZoom(this, 'locality', 12, done);
});
it('should zoom to 11 when search result is localadmin type', function (done) {
testZoom(this, 'localadmin', 11, done);
});
it('should zoom to 15 when search result is neighbourhood type', function (done) {
testZoom(this, 'neighbourhood', 15, done);
});
it('should zoom to 12 when search result is unknown type', function (done) {
testZoom(this, 'whatever', 12, done);
});
});
describe('searchPin', function () {
beforeEach(function () {
this.view.options.searchPin = true;
});
it('should add a pin and an infowindow when search is completed', function (done) {
this.view._onSubmit(fakeEvent).then(function () {
expect(this.view._searchPin).toBeDefined();
expect(this.view._searchInfowindow).toBeDefined();
done();
}.bind(this));
});
it('should place pin in the feature center', function (done) {
this.view._onSubmit(fakeEvent).then(function () {
expect(this.view._searchPin.get('latlng')).toEqual([40.41889, -3.69194]);
done();
}.bind(this));
});
it('should display address in the search infowindow', function (done) {
this.view._onSubmit(fakeEvent).then(function () {
expect(this.view._searchInfowindow.$('.CDB-infowindow-title').text()).toBe('Madrid, Spain');
done();
}.bind(this));
});
it('should destroy/hide search pin when map is clicked', function (done) {
var view = this.view;
view._onSubmit(fakeEvent).then(function () {
expect(view._searchPin).toBeDefined();
expect(view._searchInfowindow).toBeDefined();
this.mapView.trigger('click');
setTimeout(function () {
expect(view._searchPin).toBeUndefined();
expect(view._searchInfowindow).toBeUndefined();
done();
}, 1000);
}.bind(this));
});
});
});
describe('_initializeGeocoder', function () {
it('should set geocoder from options if available', function () {
this.view = new Search({
model: this.map,
mapView: this.mapView,
geocoderService: 'mapbox',
token: 'token_from_options'
});
expect(this.view.geocoderService).toBe('mapbox');
expect(this.view.token).toBe('token_from_options');
});
it('should set geocoder from injected configuration if available', function () {
spyOn(Search.prototype, '_getGeocodingInfoFromConfig').and.returnValue({
provider: 'tomtom_fake',
token: 'fakeAPIKey'
});
this.view = new Search({
model: this.map,
mapView: this.mapView
});
expect(this.view.geocoderService).toBe('tomtom_fake');
expect(this.view.token).toBe('fakeAPIKey');
});
it('should set default geocoder if no configurations are available', function () {
Object.defineProperty(window, 'tomtomApiKey', {
value: 'fake_tomtom_key',
writable: true
});
this.view = new Search({
model: this.map,
mapView: this.mapView
});
expect(this.view.geocoderService).toBe('tomtom');
expect(this.view.token).toBe('fake_tomtom_key');
});
});
describe('_getGeocodingInfoFromConfig', function () {
it('should return an empty object if there is no geocoderConfiguration', function () {
expect(this.view._getGeocodingInfoFromConfig()).toEqual({});
});
it('should return provider and token from geocoderConfiguration', function () {
var geocoderConfiguration = {
provider: 'tomtom',
tomtom: {
search_bar_api_key: 'fakeAPIKey'
}
};
Object.defineProperty(window, 'geocoderConfiguration', {
value: geocoderConfiguration,
writable: true
});
expect(this.view._getGeocodingInfoFromConfig()).toEqual({
provider: 'tomtom', token: 'fakeAPIKey'
});
});
it('should return undefined provider and token if no provider found in geocoderConfiguration', function () {
var geocoderConfiguration = {};
Object.defineProperty(window, 'geocoderConfiguration', {
value: geocoderConfiguration,
writable: true
});
expect(this.view._getGeocodingInfoFromConfig()).toEqual({
provider: undefined, token: undefined
});
});
});
afterEach(function () {
this.$el.remove();
});
});
+173
View File
@@ -0,0 +1,173 @@
var $ = require('jquery');
var Backbone = require('backbone');
var Map = require('../../../../src/geo/map');
var LeafletMapView = require('../../../../src/geo/leaflet/leaflet-map-view');
var TooltipModel = require('../../../../src/geo/ui/tooltip-model');
var TooltipView = require('../../../../src/geo/ui/tooltip-view');
describe('geo/ui/tooltip-view', function () {
beforeEach(function () {
this.container = $("<div id='map'>").css('height', '1000px');
$('body').append(this.container);
var map = new Map(null, { layersFactory: {} });
this.mapView = new LeafletMapView({
el: $('#map'),
mapModel: map,
engine: new Backbone.Model(),
layerViewFactory: jasmine.createSpyObj('layerViewFactory', ['createLayerView']),
layerGroupModel: new Backbone.Model()
});
this.mapView.render();
this.layerView = new Backbone.View();
this.tooltipModel = new TooltipModel({
template: '{{#fields}}{{{ value }}},{{/fields}}'
});
this.tooltipView = new TooltipView({
model: this.tooltipModel,
layerView: this.layerView,
mapView: this.mapView
});
});
afterEach(function () {
$('#map').remove();
});
it('should render fields in specified order', function () {
this.tooltipModel.set('fields', [{
name: 'test2'
}, {
name: 'test1'
}, {
name: 'huracan'
}]);
this.tooltipView.model.updateContent({
test1: 'test1',
test2: 'test2',
huracan: 'huracan'
});
expect(this.tooltipView.$el.html()).toEqual('test2,test1,huracan,');
});
it('should use alternate_names ', function () {
this.tooltipModel.set('template', '{{#fields}}{{{ title }}},{{/fields}}');
this.tooltipModel.set('fields', [{
name: 'test2',
title: true
}, {
name: 'test1',
title: true
}, {
name: 'huracan',
title: true
}]);
this.tooltipModel.set('alternative_names', {
'test1': 'testnamed'
});
this.tooltipView.model.updateContent({
test1: 'test1',
test2: 'test2',
huracan: 'huracan'
});
expect(this.tooltipView.$el.html()).toEqual('test2,testnamed,huracan,');
});
it('should position the element correctly', function () {
this.tooltipView.$el.css('width', '200px');
this.tooltipView.$el.css('height', '20px');
this.tooltipModel.set('placement', 'bottom|right');
this.tooltipModel.setPosition({ x: 10, y: 10 });
expect(this.tooltipView.$el.css('top')).toBe('10px');
expect(this.tooltipView.$el.css('left')).toBe('10px');
this.tooltipModel.set('placement', 'top|left');
this.tooltipModel.setPosition({ x: 210, y: 40 });
expect(this.tooltipView.$el.css('top')).toBe('20px');
expect(this.tooltipView.$el.css('left')).toBe('10px');
this.tooltipModel.set('placement', 'middle|center');
this.tooltipModel.setPosition({ x: 150, y: 30 });
expect(this.tooltipView.$el.css('top')).toBe('20px');
expect(this.tooltipView.$el.css('left')).toBe('50px');
// With offsets
this.tooltipModel.set('placement', 'middle|center');
this.tooltipModel.set('offset', [-10, -10]);
this.tooltipModel.setPosition({ x: 0, y: 0 });
this.tooltipModel.setPosition({ x: 150, y: 30 });
expect(this.tooltipView.$el.css('top')).toBe('10px');
expect(this.tooltipView.$el.css('left')).toBe('40px');
});
describe('overflow positioning', function () {
beforeEach(function () {
$('#map').css('height', '100px');
$('#map').css('width', '100px');
this.tooltipView.$el.css('height', '80px');
this.tooltipView.$el.css('width', '80px');
this.mapView.invalidateSize();
});
it('should position the element on top when bottom overflow occurs', function () {
this.tooltipModel.set('placement', 'bottom|right');
this.tooltipModel.setPosition({ x: 0, y: 90 });
expect(this.tooltipView.$el.css('top')).toBe('10px');
});
it('should position the element on the bottom when top overflow occurs', function () {
this.tooltipModel.set('placement', 'top|right');
this.tooltipModel.setPosition({ x: 0, y: 10 });
expect(this.tooltipView.$el.css('top')).toBe('10px');
});
it('should position the element on top/bottom when overflow vertically centered and overflow occurs', function () {
this.tooltipModel.set('placement', 'middle|right');
this.tooltipModel.setPosition({ x: 0, y: 90 });
expect(this.tooltipView.$el.css('top')).toBe('10px');
this.tooltipModel.set('placement', 'middle|right');
this.tooltipModel.setPosition({ x: 0, y: 10 });
expect(this.tooltipView.$el.css('top')).toBe('10px');
});
it('should position the element on the left when right overflow occurs', function () {
this.tooltipModel.set('placement', 'top|right');
this.tooltipModel.setPosition({ x: 90, y: 10 });
expect(this.tooltipView.$el.css('left')).toBe('10px');
});
it('should position the element on the right when left overflow occurs', function () {
this.tooltipModel.set('placement', 'top|left');
this.tooltipModel.setPosition({ x: 10, y: 10 });
expect(this.tooltipView.$el.css('left')).toBe('10px');
});
it('should position the element on the left/right when horizontally centered and overflow occurs', function () {
this.tooltipModel.set('placement', 'top|center');
this.tooltipModel.setPosition({ x: 10, y: 10 });
expect(this.tooltipView.$el.css('left')).toBe('10px');
this.tooltipModel.set('placement', 'top|center');
this.tooltipModel.setPosition({ x: 90, y: 10 });
expect(this.tooltipView.$el.css('left')).toBe('10px');
});
});
});
+84
View File
@@ -0,0 +1,84 @@
var Map = require('../../../../src/geo/map');
var Zoom = require('../../../../src/geo/ui/zoom/zoom-view');
var Template = require('../../../../src/core/template');
describe('geo/ui/zoom', function () {
beforeEach(function () {
this.map = new Map(null, { layersFactory: {} });
spyOn(this.map, 'bind').and.callThrough();
spyOn(this.map, 'setZoom').and.callThrough();
this.view = new Zoom({
model: this.map,
template: Template.compile(' ' +
'<div class="CDB-Overlay">' +
'<button class="CDB-Zoom-action CDB-Zoom-action--out js-zoomOut"></button>' +
'<button class="CDB-Zoom-action CDB-Zoom-action--in js-zoomIn"></button>' +
'</div>' +
'<div class="CDB-Zoom-info">-</div>')
});
this.view.render();
});
it('should have bindings when map zoom has any change', function () {
expect(this.map.bind.calls.argsFor(0)[0]).toEqual('change:zoom change:minZoom change:maxZoom');
expect(this.map.bind.calls.argsFor(0)[1]).toEqual(this.view._checkZoom);
});
describe('zoom in', function () {
beforeEach(function () {
this.$zoomIn = this.view.$('.js-zoomIn');
});
it('should zoom in when button is clicked', function () {
this.$zoomIn.click();
expect(this.map.setZoom).toHaveBeenCalled();
expect(this.$zoomIn.hasClass('is-disabled')).toBeFalsy();
});
it('should not zoom in if max zoom is reached', function () {
this.map.set({
zoom: 3,
maxZoom: 3
});
this.$zoomIn.click();
expect(this.map.setZoom).not.toHaveBeenCalled();
});
it('should add a is-disabled class when it is not possible to zoom in', function () {
this.map.set({
zoom: 3,
maxZoom: 3
});
expect(this.$zoomIn.hasClass('is-disabled')).toBeTruthy();
});
});
describe('zoom out', function () {
beforeEach(function () {
this.$zoomOut = this.view.$('.js-zoomOut');
});
it('should zoom out when button is clicked', function () {
this.$zoomOut.click();
expect(this.map.setZoom).toHaveBeenCalled();
expect(this.$zoomOut.hasClass('is-disabled')).toBeFalsy();
});
it('should not zoom in if min zoom is reached', function () {
this.map.set({
zoom: 3,
minZoom: 3
});
this.$zoomOut.click();
expect(this.map.setZoom).not.toHaveBeenCalled();
});
it('should add a is-disabled class when it is not possible to zoom out', function () {
this.map.set({
zoom: 3,
minZoom: 3
});
expect(this.$zoomOut.hasClass('is-disabled')).toBeTruthy();
});
});
});