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
+59
View File
@@ -0,0 +1,59 @@
var DataviewsCollection = require('../../../src/dataviews/dataviews-collection');
var DataviewModel = require('../../../src/dataviews/dataview-model-base');
var whenAllDataviewsFetched = require('../../../src/vis/dataviews-tracker');
var MockFactory = require('../../helpers/mockFactory');
describe('src/vis/dataviews-tracker', function () {
var options = {
map: {},
engine: {}
};
beforeEach(function () {
this.dataviewsFetched = jasmine.createSpy('dataviewsFetched');
this.source = MockFactory.createAnalysisModel({ id: 'a1' });
this.onDataviewsfetched = jasmine.createSpy('onDataviewsfetched');
this.dataviewsCollection = new DataviewsCollection([]);
whenAllDataviewsFetched(this.dataviewsCollection, this.onDataviewsfetched);
});
it('should check fetchStatus when any dataviews changes', function () {
var dataview1 = new DataviewModel({
source: this.source
}, options);
this.dataviewsCollection.add(dataview1);
var dataview2 = new DataviewModel({
source: this.source
}, options);
this.dataviewsCollection.add(dataview2);
this.onDataviewsfetched.calls.reset();
dataview1.set('status', 'fetched');
expect(this.onDataviewsfetched).not.toHaveBeenCalled();
});
it('should trigger dataviewsFetched event when all dataviews fetches', function () {
var dataview1 = new DataviewModel({
source: this.source
}, options);
this.dataviewsCollection.add(dataview1);
var dataview2 = new DataviewModel({
source: this.source
}, options);
this.dataviewsCollection.add(dataview2);
this.onDataviewsfetched.calls.reset();
dataview1.set('status', 'fetched');
dataview2.set('status', 'fetched');
expect(this.onDataviewsfetched).toHaveBeenCalled();
});
});
+578
View File
@@ -0,0 +1,578 @@
var Backbone = require('backbone');
var Map = require('../../../src/geo/map');
var Engine = require('../../../src/engine');
var CartoDBLayer = require('../../../src/geo/map/cartodb-layer');
var CartoDBLayerGroup = require('../../../src/geo/cartodb-layer-group');
var TooltipModel = require('../../../src/geo/ui/tooltip-model');
var InfowindowModel = require('../../../src/geo/ui/infowindow-model');
var InfowindowManager = require('../../../src/vis/infowindow-manager');
var createEngine = require('../fixtures/engine.fixture.js');
var createCartoDBLayer = function (engine, infowindowAttrs) {
infowindowAttrs = infowindowAttrs || {
fields: [{
'name': 'name',
'title': true,
'position': 1
}],
template: '<p>{{ name }}</p>'
};
return new CartoDBLayer({
infowindow: infowindowAttrs
}, { engine: engine });
};
var simulateFeatureClickEvent = function (layerView, data) {
layerView.trigger('featureClick', {
layer: layerView.model.getLayerInLayerGroupAt(data.layerIndex),
layerIndex: data.layerIndex,
latlng: [100, 200],
position: { x: 20, y: 30 },
feature: data.data
});
};
describe('src/vis/infowindow-manager.js', function () {
var engineMock;
beforeEach(function () {
this.map = new Map({}, {
layersFactory: {}
});
this.infowindowModel = new InfowindowModel();
this.tooltipModel = new TooltipModel();
this.cartoDBLayerGroup = new CartoDBLayerGroup({}, {
layersCollection: this.map.layers
});
this.layerView = new Backbone.View({
model: this.cartoDBLayerGroup
});
spyOn(this.cartoDBLayerGroup, 'fetchAttributes').and.callFake(function (layerIndex, featureId, callback) {
var data = { name: 'juan' };
callback(data);
});
engineMock = createEngine();
this.infowindowManager = new InfowindowManager({ // eslint-disable-line
engine: engineMock,
mapModel: this.map,
infowindowModel: this.infowindowModel,
tooltipModel: this.tooltipModel
});
});
it('should correctly bind the featureClick event to the corresponding layerView', function () {
var layer1 = createCartoDBLayer(engineMock, {
template: 'template1',
template_type: 'underscore',
fields: [{
'name': 'name',
'title': true,
'position': 1
}],
alternative_names: 'alternative_names1'
});
var layer2 = createCartoDBLayer(engineMock, {
template: 'template2',
template_type: 'underscore',
fields: [{
'name': 'description',
'title': true,
'position': 1
}],
alternative_names: 'alternative_names2'
});
this.infowindowManager.start(this.layerView);
this.map.layers.reset([ layer1, layer2 ]);
simulateFeatureClickEvent(this.layerView, {
layerIndex: 0,
data: { cartodb_id: 10 }
});
// A request to fetch the attributes for the right cartodb_id and layerIndex has been triggered
expect(this.layerView.model.fetchAttributes.calls.count()).toEqual(1);
expect(this.layerView.model.fetchAttributes).toHaveBeenCalledWith(0, 10, jasmine.any(Function));
this.layerView.model.fetchAttributes.calls.reset();
// InfowindowModel has been updated
expect(this.infowindowModel.attributes).toEqual({
'template': 'template1',
'alternative_names': 'alternative_names1',
'template_type': 'underscore',
'offset': [
28,
0
],
'maxHeight': 180,
'autoPan': true,
'content': {
'fields': [
{
'name': 'name',
'title': 'name',
'value': 'juan',
'index': 0
}
],
'data': {
'name': 'juan'
}
},
'latlng': [
100,
200
],
'visibility': true,
'currentFeatureId': 10
});
simulateFeatureClickEvent(this.layerView, {
layerIndex: 1,
data: { cartodb_id: 100 }
});
// A request to fetch the attributes for the right cartodb_id and layerIndex has been triggered
expect(this.layerView.model.fetchAttributes.calls.count()).toEqual(1);
expect(this.layerView.model.fetchAttributes).toHaveBeenCalledWith(1, 100, jasmine.any(Function));
this.layerView.model.fetchAttributes.calls.reset();
// InfowindowModel has been updated
expect(this.infowindowModel.attributes).toEqual({
'template': 'template2',
'alternative_names': 'alternative_names2',
'template_type': 'underscore',
'offset': [
28,
0
],
'maxHeight': 180,
'autoPan': true,
'content': {
'fields': [
{
'title': null,
'value': 'No data available',
'index': 0,
'type': 'empty'
}
],
'data': {
'name': 'juan'
}
},
'latlng': [
100,
200
],
'visibility': true,
'currentFeatureId': 100
});
});
it('should not fetch attributes and show the infowindow if template is empty', function () {
var layer1 = createCartoDBLayer(engineMock, {
template: '',
template_type: 'underscore',
fields: [{
'name': 'name',
'title': true,
'position': 1
}],
alternative_names: 'alternative_names1'
});
this.infowindowManager.start(this.layerView);
this.map.layers.reset([ layer1 ]);
simulateFeatureClickEvent(this.layerView, {
layerIndex: 0,
data: { cartodb_id: 10 }
});
expect(this.layerView.model.fetchAttributes).not.toHaveBeenCalled();
expect(this.infowindowModel.get('visibility')).toEqual(false);
});
it('should not fetch attributes and show the infowindow if popups are disabled', function () {
var layer1 = createCartoDBLayer(engineMock, {
template: 'template1',
template_type: 'underscore',
fields: [{
'name': 'name',
'title': true,
'position': 1
}],
alternative_names: 'alternative_names1'
});
this.infowindowManager.start(this.layerView);
this.map.layers.reset([ layer1 ]);
this.map.disablePopups();
simulateFeatureClickEvent(this.layerView, {
layerIndex: 0,
data: { cartodb_id: 10 }
});
expect(this.layerView.model.fetchAttributes).not.toHaveBeenCalled();
expect(this.infowindowModel.get('visibility')).toEqual(false);
});
it('should hide the infowindow if map popups are disabled', function () {
var layer1 = createCartoDBLayer(engineMock, {
template: 'template1',
template_type: 'underscore',
fields: [{
'name': 'name',
'title': true,
'position': 1
}],
alternative_names: 'alternative_names1'
});
this.infowindowManager.start(this.layerView);
this.map.layers.reset([ layer1 ]);
simulateFeatureClickEvent(this.layerView, {
layerIndex: 0,
data: { cartodb_id: 10 }
});
expect(this.infowindowModel.get('visibility')).toEqual(true);
// Disable Map Popups
this.map.disablePopups();
expect(this.infowindowModel.get('visibility')).toEqual(false);
});
it('should set loading content while loading', function () {
var layer1 = createCartoDBLayer(engineMock);
this.infowindowManager.start(this.layerView);
this.map.layers.reset([ layer1 ]);
// Fetch attributes does NOT suceed inmediatily
this.layerView.model.fetchAttributes.and.callFake(function () {});
simulateFeatureClickEvent(this.layerView, {
layerIndex: 0,
data: { cartodb_id: 10 }
});
// InfowindowModel has been updated
expect(this.infowindowModel.get('content').fields).toEqual([
{
'type': 'loading',
'title': 'loading',
'value': '…'
}
]);
expect(this.infowindowModel.get('visibility')).toBe(true);
});
it('should set error content if no attributes are returned', function () {
var layer1 = new CartoDBLayer({
infowindow: {
template: 'template1',
template_type: 'underscore',
fields: [{
'name': 'name',
'title': true,
'position': 1
}],
alternative_names: 'alternative_names1'
}
}, { engine: engineMock });
this.infowindowManager.start(this.layerView);
this.map.layers.reset([ layer1 ]);
this.layerView.model.fetchAttributes.and.callFake(function (layerIndex, featureId, callback) {
callback();
});
simulateFeatureClickEvent(this.layerView, {
layerIndex: 0,
data: { cartodb_id: 10 }
});
// InfowindowModel has been updated
expect(this.infowindowModel.get('content').fields).toEqual([
{
'title': null,
'alternative_name': null,
'value': 'There has been an error...',
'index': null,
'type': 'error'
}
]);
expect(this.infowindowModel.get('visibility')).toBe(true);
});
it('should set the currentFeatureId on the model when the infowindow is shown', function () {
var layer = new CartoDBLayer({
infowindow: {
template: 'template',
template_type: 'underscore',
fields: [{
'name': 'name',
'title': true,
'position': 1
}],
alternative_names: 'alternative_names'
}
}, { engine: engineMock });
this.infowindowManager.start(this.layerView);
this.map.layers.reset([ layer ]);
simulateFeatureClickEvent(this.layerView, {
layerIndex: 0,
data: { cartodb_id: 10 }
});
expect(this.infowindowModel.getCurrentFeatureId()).toEqual(10);
});
it('should hide the tooltip', function () {
var layer = new CartoDBLayer({
infowindow: {
template: 'template',
template_type: 'underscore',
fields: [{
'name': 'name',
'title': true,
'position': 1
}],
alternative_names: 'alternative_names'
}
}, { engine: engineMock });
this.infowindowManager.start(this.layerView);
this.map.layers.reset([ layer ]);
this.tooltipModel.set('visible', true);
simulateFeatureClickEvent(this.layerView, {
layerIndex: 0,
data: { cartodb_id: 10 }
});
expect(this.tooltipModel.isVisible()).toBeFalsy();
});
it('should unset the currentFeatureId on the model when the infowindow is hidden', function () {
var layer = createCartoDBLayer(engineMock);
this.infowindowManager.start(this.layerView);
this.map.layers.reset([ layer ]);
simulateFeatureClickEvent(this.layerView, {
layerIndex: 0,
data: { cartodb_id: 10 }
});
// Layer is hidden and infowindow too
layer.hide();
expect(this.infowindowModel.getCurrentFeatureId()).toBeUndefined();
});
it('should update the infowindow model when the infowindow template gets new fields', function () {
var layer = createCartoDBLayer(engineMock);
this.infowindowManager.start(this.layerView);
this.map.layers.reset([ layer ]);
simulateFeatureClickEvent(this.layerView, {
layerIndex: 0,
data: { cartodb_id: 10 }
});
spyOn(this.infowindowModel, 'setInfowindowTemplate');
layer.infowindow.update({
fields: [
{
'name': 'name',
'title': true,
'position': 1
},
{
'name': 'description',
'title': true,
'position': 2
}
]
});
expect(this.infowindowModel.setInfowindowTemplate).toHaveBeenCalledWith(layer.infowindow);
});
xit('should re-fetch attributes when the vis is reloaded', function () {
var layer = createCartoDBLayer(engineMock);
this.infowindowManager.start(this.layerView);
this.map.layers.reset([ layer ]);
this.cartoDBLayerGroup.fetchAttributes.and.callFake(function (layerIndex, featureId, callback) {
var data = { name: 'juan' };
callback(data);
});
simulateFeatureClickEvent(this.layerView, {
layerIndex: 0,
data: { cartodb_id: 10 }
});
expect(this.infowindowModel.get('content')).toEqual({
'fields': [
{
'name': 'name',
'title': 'name',
'value': 'juan',
'index': 0
}
],
'data': {
'name': 'juan'
}
});
this.cartoDBLayerGroup.fetchAttributes.and.callFake(function (layerIndex, featureId, callback) {
var data = { name: 'luis' };
callback(data);
});
engineMock._eventEmmitter.trigger(Engine.Events.RELOAD_SUCCESS);
// InfowindowModel has been updated
expect(this.infowindowModel.get('content')).toEqual({
'fields': [
{
'name': 'name',
'title': 'name',
'value': 'luis',
'index': 0
}
],
'data': {
'name': 'luis'
}
});
});
it('should hide the infowindow when fields are cleared in the infowindow template', function () {
// Simulate that the layerView has been added a tooltipView
var tooltipView = jasmine.createSpyObj('tooltipView', ['setFilter', 'hide']);
tooltipView.setFilter.and.returnValue(tooltipView);
this.layerView.tooltipView = tooltipView;
var layer1 = createCartoDBLayer(engineMock);
var layer2 = createCartoDBLayer(engineMock);
this.infowindowManager.start(this.layerView);
this.map.layers.reset([ layer1, layer2 ]);
simulateFeatureClickEvent(this.layerView, {
layerIndex: 0,
data: { cartodb_id: 10 }
});
expect(this.infowindowModel.get('visibility')).toBe(true);
// Clear fields on layer #1
layer2.infowindow.update({
fields: []
});
// Nothing happened
expect(this.infowindowModel.get('visibility')).toBe(true);
expect(engineMock.reload).not.toHaveBeenCalledWith({});
// Clear fields on layer #0 (the one that was opened)
layer1.infowindow.update({
fields: []
});
// Infowindow has been closed and map has NOT been reloaded
expect(this.infowindowModel.get('visibility')).toBe(false);
expect(engineMock.reload).not.toHaveBeenCalledWith({});
});
it('should hide the infowindow if the layer is hidden', function () {
var layer1 = createCartoDBLayer(engineMock);
var layer2 = createCartoDBLayer(engineMock);
this.infowindowManager.start(this.layerView);
this.map.layers.reset([ layer1, layer2 ]);
simulateFeatureClickEvent(this.layerView, {
layerIndex: 0,
data: { cartodb_id: 10 }
});
expect(this.infowindowModel.get('visibility')).toBe(true);
// hide layer2 -> nothing happens
layer2.hide();
expect(this.infowindowModel.get('visibility')).toBe(true);
// hide layer1 -> infowindow is hidden
layer1.hide();
expect(this.infowindowModel.get('visibility')).toBe(false);
});
describe('.fetchAttributes', function () {
it('should work as expected?', function () {
jasmine.clock().install();
var currentCall = 0;
spyOn(this.infowindowModel, 'updateContent');
this.infowindowManager._cartoDBLayerGroupView = this.layerView;
this.currentFeatureId = 10;
spyOn(this.cartoDBLayerGroup, 'getIndexOfLayerInLayerGroup').and.callFake(function () {
return currentCall++;
});
this.cartoDBLayerGroup.fetchAttributes.and.callFake(function (layerIndex, featureId, callback) {
var data = { name: 'test' };
currentCall === 1
? setTimeout(function () { callback(data); }, 100)
: callback(data);
});
this.infowindowManager._fetchAttributes(10);
this.infowindowManager._fetchAttributes(4);
jasmine.clock().tick(101);
expect(this.infowindowModel.updateContent.calls.count()).toEqual(1);
jasmine.clock().uninstall();
});
});
});
+199
View File
@@ -0,0 +1,199 @@
var _ = require('underscore');
var AnalysisModel = require('../../../src/analysis/analysis-model');
var LayersFactory = require('../../../src/vis/layers-factory');
var createEngine = require('../fixtures/engine.fixture.js');
var createFakeAnalysis = function (attrs) {
return new AnalysisModel(attrs, {
engine: {},
camshaftReference: {
getParamNamesForAnalysisType: function () {}
}
});
};
describe('vis/layers-factory', function () {
var analysis;
var layersFactory;
var engineMock;
beforeEach(function () {
engineMock = createEngine();
this.windshaftSettings = {
urlTemplate: 'http://{user}.carto.com',
userName: 'JUAN',
templateName: 'TPL_12345678',
client: 'CLIENT_VERSION',
apiKey: 'API_KEY',
authToken: 'AUTH_TOKEN'
};
layersFactory = new LayersFactory({
engine: engineMock,
windshaftSettings: this.windshaftSettings
});
analysis = createFakeAnalysis();
});
describe('attribute validation', function () {
var testCases = [
{
layerType: 'CartoDB',
// TODO: Once https://github.com/CartoDB/cartodb/issues/12885 is merged
// source should be required again so we need to add the attribute to this test
// expectedErrorMessage: 'The following attributes are missing: source,cartocss'
expectedErrorMessage: 'The following attributes are missing: cartocss'
},
{
layerType: 'torque',
// TODO: Once https://github.com/CartoDB/cartodb/issues/12885 is merged
// source should be required again so we need to add the attribute to this test
// expectedErrorMessage: 'The following attributes are missing: source,cartocss'
expectedErrorMessage: 'The following attributes are missing: cartocss'
},
{
layerType: 'Tiled',
expectedErrorMessage: 'The following attributes are missing: urlTemplate'
},
{
layerType: 'WMS',
expectedErrorMessage: 'The following attributes are missing: urlTemplate'
},
{
layerType: 'GMapsBase',
expectedErrorMessage: 'The following attributes are missing: baseType'
},
{
layerType: 'Plain',
expectedErrorMessage: 'The following attributes are missing: image|color'
}
];
_.each(testCases, function (testCase) {
describe(testCase.layerType, function () {
it('should throw an error if no properties are given', function () {
expect(function () {
layersFactory.createLayer(testCase.layerType, {});
}).toThrowError(testCase.expectedErrorMessage);
});
});
});
});
describe('tiled', function () {
it('should create the layer model', function () {
var layerModel = layersFactory.createLayer('tiled', {
urlTemplate: 'http'
});
expect(layerModel).toBeDefined();
expect(layerModel.get('type')).toEqual('Tiled');
});
_.each({
'https://dnv9my2eseobd.cloudfront.net/': 'http://a.tiles.mapbox.com/',
'https://maps.nlp.nokia.com/': 'http://maps.nlp.nokia.com/',
'https://tile.stamen.com/': 'http://tile.stamen.com/',
'https://{s}.maps.nlp.nokia.com/': 'http://{s}.maps.nlp.nokia.com/',
'https://cartocdn_{s}.global.ssl.fastly.net/': 'http://{s}.api.cartocdn.com/',
'https://cartodb-basemaps-{s}.global.ssl.fastly.net/': 'http://{s}.basemaps.cartocdn.com/'
}, function (httpUrlTemplate, httpsUrlTemplate) {
describe('when https option is set to true', function () {
beforeEach(function () {
spyOn(LayersFactory, 'isHttps').and.returnValue(true);
});
it("should not convert '" + httpsUrlTemplate + "'", function () {
var layerModel = layersFactory.createLayer('tiled', {
urlTemplate: httpsUrlTemplate
});
expect(layerModel.get('urlTemplate')).toEqual(httpsUrlTemplate);
});
it("should convert '" + httpUrlTemplate + "' to '" + httpsUrlTemplate + "'", function () {
var layerModel = layersFactory.createLayer('tiled', {
urlTemplate: httpUrlTemplate
});
expect(layerModel.get('urlTemplate')).toEqual(httpsUrlTemplate);
});
});
describe('when https option is set to false', function () {
beforeEach(function () {
spyOn(LayersFactory, 'isHttps').and.returnValue(false);
});
it("should not convert '" + httpUrlTemplate + "'", function () {
var layerModel = layersFactory.createLayer('tiled', {
urlTemplate: httpUrlTemplate
});
expect(layerModel.get('urlTemplate')).toEqual(httpUrlTemplate);
});
it("should convert '" + httpsUrlTemplate + "' to '" + httpUrlTemplate + "'", function () {
var layerModel = layersFactory.createLayer('tiled', {
urlTemplate: httpsUrlTemplate
});
expect(layerModel.get('urlTemplate')).toEqual(httpUrlTemplate);
});
});
});
});
describe('torque', function () {
it('should create the layer model', function () {
var layerModel = layersFactory.createLayer('torque', {
source: analysis,
cartocss: '#layer {}'
});
expect(layerModel).toBeDefined();
expect(layerModel.get('type')).toEqual('torque');
});
it('should setup attrs for windshaft provider', function () {
var layerModel = layersFactory.createLayer('torque', {
source: analysis,
cartocss: '#layer {}'
});
expect(layerModel.get('user_name')).toEqual('JUAN');
expect(layerModel.get('maps_api_template')).toEqual('http://{user}.carto.com');
expect(layerModel.get('client')).toEqual('CLIENT_VERSION');
expect(layerModel.get('named_map')).toEqual({
name: 'TPL_12345678'
});
expect(layerModel.get('api_key')).toEqual('API_KEY');
expect(layerModel.get('auth_token')).toEqual('AUTH_TOKEN');
});
it("should not include a named_map attr if settings don't have one", function () {
delete this.windshaftSettings.templateName;
var layerModel = layersFactory.createLayer('torque', {
source: analysis,
cartocss: '#layer {}'
});
expect(layerModel.get('named_map')).toBeUndefined();
});
});
['cartodb', 'torque'].forEach(function (layerType) {
describe(layerType + ' (shared)', function () {
it('should set accept an AnalysisMode as a source', function () {
var layerModel = layersFactory.createLayer(layerType, {
source: analysis,
cartocss: '#layer {}'
});
expect(layerModel.getSource()).toEqual(analysis);
});
});
});
});
+146
View File
@@ -0,0 +1,146 @@
var Backbone = require('backbone');
var MapCursorManager = require('../../../src/vis/map-cursor-manager');
var CartoDBLayer = require('../../../src/geo/map/cartodb-layer');
var createLayerModel = function () {
var layerModel = new CartoDBLayer(null, { engine: { reload: function () {}, on: function () {} } });
layerModel.isInfowindowEnabled = jasmine.createSpy('isInfowindowEnabled');
return layerModel;
};
describe('src/vis/map-cursor-manager.js', function () {
beforeEach(function () {
this.mapView = jasmine.createSpyObj('mapView', ['setCursor']);
this.mapModel = jasmine.createSpyObj('map', ['arePopupsEnabled', 'isFeatureInteractivityEnabled']);
this.layerModel = createLayerModel();
this.cartoDBLayerGroupView = new Backbone.View();
this.mapCursorManager = new MapCursorManager({
mapView: this.mapView,
mapModel: this.mapModel
});
this.mapCursorManager.start(this.cartoDBLayerGroupView);
});
describe('when a feature is overed', function () {
beforeEach(function () {
this.mapModel.arePopupsEnabled.and.returnValue(false);
this.mapModel.isFeatureInteractivityEnabled.and.returnValue(false);
this.layerModel.isInfowindowEnabled.and.returnValue(false);
});
it('should NOT change the cursor to pointer if layer is not clickable', function () {
this.cartoDBLayerGroupView.trigger('featureOver', {
layer: this.layerModel
});
expect(this.mapView.setCursor).not.toHaveBeenCalled();
});
it('should change the cursor to pointer if feature interactivity is enabled', function () {
this.mapModel.isFeatureInteractivityEnabled.and.returnValue(true);
this.cartoDBLayerGroupView.trigger('featureOver', {
layer: this.layerModel
});
expect(this.mapView.setCursor).toHaveBeenCalledWith('pointer');
});
describe('when popups are enabled', function () {
beforeEach(function () {
this.mapModel.arePopupsEnabled.and.returnValue(true);
});
it('should NOT change the cursor to pointer if layer has NO infowindow enabled', function () {
this.cartoDBLayerGroupView.trigger('featureOver', {
layer: this.layerModel
});
expect(this.mapView.setCursor).not.toHaveBeenCalled();
});
it('should change the cursor to pointer if layer infowindows enabled', function () {
this.layerModel.isInfowindowEnabled.and.returnValue(true);
this.cartoDBLayerGroupView.trigger('featureOver', {
layer: this.layerModel
});
expect(this.mapView.setCursor).toHaveBeenCalledWith('pointer');
});
});
});
describe('when a feature is not overed anymore', function () {
beforeEach(function () {
this.mapModel.isFeatureInteractivityEnabled.and.returnValue(true);
this.cartoDBLayerGroupView.trigger('featureOver', {
layer: this.layerModel
});
expect(this.mapView.setCursor).toHaveBeenCalledWith('pointer');
this.mapView.setCursor.calls.reset();
});
it('should change the cursor to auto if no other layer is being overed', function () {
this.cartoDBLayerGroupView.trigger('featureOut', {
layer: this.layerModel
});
expect(this.mapView.setCursor).toHaveBeenCalledWith('auto');
});
it('should change the cursor to pointer if other clickable layers are still being overed', function () {
var anotherLayerModel = createLayerModel();
// Another layer is being overed
this.cartoDBLayerGroupView.trigger('featureOver', {
layer: anotherLayerModel
});
expect(this.mapView.setCursor).toHaveBeenCalledWith('pointer');
expect(this.mapView.setCursor.calls.reset());
// First layer is not overed anymore
this.cartoDBLayerGroupView.trigger('featureOut', {
layer: this.layerModel
});
expect(this.mapView.setCursor).toHaveBeenCalledWith('pointer');
});
});
describe('when a feature was overed and layer visibility changes', function () {
beforeEach(function () {
this.mapModel.isFeatureInteractivityEnabled.and.returnValue(true);
this.cartoDBLayerGroupView.trigger('featureOver', {
layer: this.layerModel
});
expect(this.mapView.setCursor).toHaveBeenCalledWith('pointer');
});
it('should change the cursor to auto if layer is hidden', function () {
this.mapView.setCursor.calls.reset();
this.layerModel.set('visible', false);
expect(this.mapView.setCursor).toHaveBeenCalledWith('auto');
});
it('should keep the cursor as pointer if layer is hidden but other clickable layers are visible', function () {
var anotherLayerModel = createLayerModel();
// Another layer is being overed
this.cartoDBLayerGroupView.trigger('featureOver', {
layer: anotherLayerModel
});
this.mapView.setCursor.calls.reset();
this.layerModel.set('visible', false);
expect(this.mapView.setCursor).toHaveBeenCalledWith('pointer');
});
});
});
+63
View File
@@ -0,0 +1,63 @@
var _ = require('underscore');
var Backbone = require('backbone');
var OverlaysFactory = require('../../../src/vis/overlays-factory');
describe('vis/overlays-factory', function () {
beforeEach(function () {
this.map = new Backbone.Model();
this.map.layers = new Backbone.Collection();
this.map.getMapViewSize = function () {
return { x: 650, y: 1000 };
};
this.mapView = new Backbone.View();
this.mapView.map = this.map;
this.mapView.latLngToContainerPoint = function () {
return { x: 100, y: 200 };
};
this.visView = new Backbone.View();
this.visView.mapView = this.mapView;
this.overlaysFactory = new OverlaysFactory({
mapModel: this.map,
mapView: this.mapView,
visView: this.visView
});
});
it('should register and create a type', function () {
var spy = jasmine.createSpy('spy').and.callFake(function (data, deps) {
return {};
});
OverlaysFactory.register('test', spy);
var overlay = this.overlaysFactory.create('test', { a: 'something' });
expect(spy).toHaveBeenCalledWith({
a: 'something',
options: {}
}, {
mapModel: this.map,
mapView: this.mapView,
visView: this.visView
});
expect(overlay.type).toEqual('test');
});
_.each([
{ type: 'logo', data: {} },
{ type: 'attribution', data: {} },
{ type: 'zoom', data: {} },
{ type: 'loader', data: {} },
{ type: 'fullscreen', data: {} },
{ type: 'search', data: {} }
], function (testCase) {
var overlayType = testCase.type;
var overlayData = testCase.data;
it('should create a "' + overlayType + '" overlay', function () {
var overlay = this.overlaysFactory.create(overlayType, overlayData);
expect(overlay).toBeDefined();
});
}, this);
});
+343
View File
@@ -0,0 +1,343 @@
var Backbone = require('backbone');
var Map = require('../../../src/geo/map');
var CartoDBLayer = require('../../../src/geo/map/cartodb-layer');
var CartoDBLayerGroup = require('../../../src/geo/cartodb-layer-group');
var TooltipModel = require('../../../src/geo/ui/tooltip-model');
var InfowindowModel = require('../../../src/geo/ui/infowindow-model');
var TooltipManager = require('../../../src/vis/tooltip-manager');
var createEngine = require('../fixtures/engine.fixture.js');
var simulateFeatureOverEvent = function (layerView, data) {
layerView.trigger('featureOver', {
layer: layerView.model.getLayerInLayerGroupAt(data.layerIndex),
layerIndex: data.layerIndex,
latlng: [100, 200],
position: data.position || { x: 20, y: 30 },
feature: data.data
});
};
var simulateFeatureOutEvent = function (layerView, data) {
layerView.trigger('featureOut', {
layer: layerView.model.getLayerInLayerGroupAt(data.layerIndex),
layerIndex: data.layerIndex
});
};
describe('src/vis/tooltip-manager.js', function () {
var engineMock;
beforeEach(function () {
this.map = new Map(null, { layersFactory: {} });
this.layerView = new Backbone.View({
model: new CartoDBLayerGroup({}, {
layersCollection: this.map.layers
})
});
engineMock = createEngine();
this.tooltipModel = new TooltipModel();
this.infowindowModel = new InfowindowModel();
this.tooltipManager = new TooltipManager({ // eslint-disable-line
engine: engineMock,
mapModel: this.map,
tooltipModel: this.tooltipModel,
infowindowModel: this.infowindowModel
});
});
it('should show the tooltip when a feature is hovered', function () {
var layer1 = new CartoDBLayer({
tooltip: {
template: 'template1',
template_type: 'underscore',
fields: [{
'name': 'name',
'title': true,
'position': 1
}],
alternative_names: 'alternative_names1'
}
}, { engine: engineMock });
var layer2 = new CartoDBLayer({
tooltip: {
template: 'template2',
template_type: 'underscore',
fields: [{
'name': 'description',
'title': true,
'position': 1
}],
alternative_names: 'alternative_names2'
}
}, { engine: engineMock });
this.tooltipManager.start(this.layerView);
this.map.layers.reset([ layer1, layer2 ]);
simulateFeatureOverEvent(this.layerView, {
layerIndex: 0,
position: { x: 100, y: 200 },
data: { cartodb_id: 10, name: 'CARTO' }
});
expect(this.tooltipModel.get('position')).toEqual({ x: 100, y: 200 });
expect(this.tooltipModel.get('fields')).toEqual([{
'name': 'name',
'title': true,
'position': 1
}]);
expect(this.tooltipModel.get('template')).toEqual('template1');
expect(this.tooltipModel.get('alternative_names')).toEqual('alternative_names1');
expect(this.tooltipModel.isVisible()).toBeTruthy();
simulateFeatureOverEvent(this.layerView, {
layerIndex: 1,
position: { x: 200, y: 300 },
data: { cartodb_id: 10 }
});
expect(this.tooltipModel.get('position')).toEqual({ x: 200, y: 300 });
expect(this.tooltipModel.get('fields')).toEqual([{
'name': 'description',
'title': true,
'position': 1
}]);
expect(this.tooltipModel.get('template')).toEqual('template2');
expect(this.tooltipModel.get('alternative_names')).toEqual('alternative_names2');
expect(this.tooltipModel.isVisible()).toBeTruthy();
});
it('should NOT show the tooltip when a feature is hovered and infowindow is opened', function () {
var layer1 = new CartoDBLayer({
tooltip: {
template: 'template1',
template_type: 'underscore',
fields: [{
'name': 'name',
'title': true,
'position': 1
}],
alternative_names: 'alternative_names1'
}
}, { engine: engineMock });
this.tooltipManager.start(this.layerView);
this.map.layers.reset([ layer1 ]);
simulateFeatureOverEvent(this.layerView, {
layerIndex: 0,
data: { cartodb_id: 10, name: 'CARTO' }
});
expect(this.tooltipModel.isVisible()).toBeTruthy();
simulateFeatureOutEvent(this.layerView, {
layerIndex: 0
});
expect(this.tooltipModel.isVisible()).toBeFalsy();
// Infowindow for layer1 is now visible
this.infowindowModel.setInfowindowTemplate(layer1.infowindow);
this.infowindowModel.setCurrentFeatureId(10);
this.infowindowModel.show();
simulateFeatureOverEvent(this.layerView, {
layerIndex: 0,
data: { cartodb_id: 10, name: 'CARTO' }
});
expect(this.tooltipModel.isVisible()).toBeFalsy();
});
it('should hide the tooltip when a feature is not hovered anymore', function () {
var layer1 = new CartoDBLayer({
tooltip: {
template: 'template1',
template_type: 'underscore',
fields: [{
'name': 'name',
'title': true,
'position': 1
}],
alternative_names: 'alternative_names1'
}
}, { engine: engineMock });
this.tooltipManager.start(this.layerView);
this.map.layers.reset([ layer1 ]);
simulateFeatureOverEvent(this.layerView, {
layerIndex: 0,
data: { cartodb_id: 10, name: 'CARTO' }
});
expect(this.tooltipModel.isVisible()).toBeTruthy();
simulateFeatureOutEvent(this.layerView, {
layerIndex: 0
});
expect(this.tooltipModel.isVisible()).toBeFalsy();
});
it('should hide the tooltip when the layer is hidden', function () {
var layer1 = new CartoDBLayer({
tooltip: {
template: 'template1',
template_type: 'underscore',
fields: [{
'name': 'name',
'title': true,
'position': 1
}],
alternative_names: 'alternative_names1'
}
}, { engine: engineMock });
this.tooltipManager.start(this.layerView);
this.map.layers.reset([ layer1 ]);
simulateFeatureOverEvent(this.layerView, {
layerIndex: 0,
data: { cartodb_id: 10, name: 'CARTO' }
});
expect(this.tooltipModel.isVisible()).toBeTruthy();
layer1.hide();
expect(this.tooltipModel.isVisible()).toBeFalsy();
});
it('should NOT hide the tooltip when a feature is being featured over and other features are outered', function () {
var layer1 = new CartoDBLayer({
tooltip: {
template: 'template1',
template_type: 'underscore',
fields: [{
'name': 'name',
'title': true,
'position': 1
}],
alternative_names: 'alternative_names1'
}
}, { engine: engineMock });
var layer2 = new CartoDBLayer({
tooltip: {
template: 'template2',
template_type: 'underscore',
fields: [{
'name': 'name',
'title': true,
'position': 1
}],
alternative_names: 'alternative_names1'
}
}, { engine: engineMock });
this.tooltipManager.start(this.layerView);
this.map.layers.reset([ layer1, layer2 ]);
simulateFeatureOverEvent(this.layerView, {
layerIndex: 0,
data: { cartodb_id: 10, name: 'CARTO' }
});
expect(this.tooltipModel.isVisible()).toBeTruthy();
// A feature from a different layer is not overed anymore
simulateFeatureOutEvent(this.layerView, {
layerIndex: 1
});
expect(this.tooltipModel.isVisible()).toBeTruthy();
});
it('should NOT show the tooltip if popups are disabled', function () {
var layer1 = new CartoDBLayer({
tooltip: {
template: 'template1',
template_type: 'underscore',
fields: [{
'name': 'name',
'title': true,
'position': 1
}],
alternative_names: 'alternative_names1'
}
}, { engine: engineMock });
this.tooltipManager.start(this.layerView);
this.map.layers.reset([ layer1 ]);
this.map.disablePopups();
simulateFeatureOverEvent(this.layerView, {
layerIndex: 0,
data: { cartodb_id: 10, name: 'CARTO' }
});
expect(this.tooltipModel.isVisible()).toBeFalsy();
});
it('should NOT show the tooltip if tooltip has no template', function () {
var layer1 = new CartoDBLayer({
tooltip: {
template: '',
template_type: 'underscore',
fields: [{
'name': 'name',
'title': true,
'position': 1
}],
alternative_names: 'alternative_names1'
}
}, { engine: engineMock });
this.tooltipManager.start(this.layerView);
this.map.layers.reset([ layer1 ]);
simulateFeatureOverEvent(this.layerView, {
layerIndex: 0,
data: { cartodb_id: 10, name: 'CARTO' }
});
expect(this.tooltipModel.isVisible()).toBeFalsy();
});
it('should NOT show the tooltipView if the layerModel doesn\'t have tooltip data', function () {
var layer1 = new CartoDBLayer({}, { engine: engineMock });
var layer2 = new CartoDBLayer({
tooltip: {
template: 'template2',
template_type: 'underscore',
fields: [{
'name': 'description',
'title': true,
'position': 1
}],
alternative_names: 'alternative_names2'
}
}, { engine: engineMock });
this.tooltipManager.start(this.layerView);
this.map.layers.reset([ layer1, layer2 ]);
simulateFeatureOverEvent(this.layerView, {
layerIndex: 0,
data: { cartodb_id: 10, name: 'CARTO' }
});
expect(this.tooltipModel.isVisible()).toBeFalsy();
});
});
+140
View File
@@ -0,0 +1,140 @@
var $ = require('jquery');
var Backbone = require('backbone');
var VisView = require('../../../src/vis/vis-view');
var VisModel = require('../../../src/vis/vis');
var VizJSON = require('../../../src/api/vizjson');
var Engine = require('../../../src/engine');
var MapViewFactory = require('../../../src/geo/map-view-factory');
var createVisView = function (container, visModel, settingsModel) {
var options = {
el: container,
widgets: new Backbone.Collection(),
model: visModel,
settingsModel: settingsModel
};
return new VisView(options);
};
describe('vis/vis-view', function () {
beforeEach(function () {
spyOn(MapViewFactory, 'createMapView').and.returnValue(jasmine.createSpyObj('fakeMapView', ['render', 'clean', 'bind']));
this.container = $('<div>').css('height', '200px');
this.mapConfig = {
updated_at: 'cachebuster',
title: 'irrelevant',
description: 'not so irrelevant',
url: 'https://carto.com',
center: [40.044, -101.95],
zoom: 4,
bounds: [
[1, 2],
[3, 4]
],
user: {
fullname: 'Chuck Norris',
avatar_url: 'http://example.com/avatar.jpg'
},
datasource: {
user_name: 'wadus',
maps_api_template: 'https://{user}.example.com:443',
stat_tag: 'ece6faac-7271-11e5-a85f-04013fc66a01'
}
};
this.visModel = new VisModel();
this.settingsModel = new Backbone.Model({
showLegends: true,
showLayerSelector: true
});
this.visView = createVisView(this.container, this.visModel, this.settingsModel);
this.visModel.load(new VizJSON(this.mapConfig));
this.visModel.map.setMapViewSize({ x: 650, y: 1000 });
this.visView.render();
});
afterEach(function () {
jasmine.clock().uninstall();
});
describe('map provider', function () {
it('should have created a LeafletMap by default', function () {
expect(MapViewFactory.createMapView.calls.mostRecent().args[0]).toEqual('leaflet');
});
it('should have created a LeafletMap when map provider is "leaflet"', function () {
this.visModel.map.set('provider', 'leaflet');
expect(MapViewFactory.createMapView).toHaveBeenCalledWith('leaflet', jasmine.anything());
});
it('should have created a GoogleMap when map provider is "google"', function () {
this.visModel.map.set('provider', 'googlemaps');
expect(MapViewFactory.createMapView).toHaveBeenCalledWith('googlemaps', jasmine.anything());
});
});
var INVALIDATE_SIZE_WAIT_IN_MS = 160;
it('should center map to origin once when map height is 0 initially and window is resized', function () {
jasmine.clock().install();
spyOn(this.visModel, 'invalidateSize');
var container = $('<div>').css('height', '0');
this.visView = createVisView(container, this.visModel, this.settingsModel);
this.visView.render();
// First time the window is resized -> map is centered to origin
$(window).trigger('resize');
jasmine.clock().tick(INVALIDATE_SIZE_WAIT_IN_MS);
expect(this.visModel.invalidateSize).toHaveBeenCalled();
this.visModel.invalidateSize.calls.reset();
// Second time the window is resized -> map is NOT centered to origin
$(window).trigger('resize');
jasmine.clock().tick(INVALIDATE_SIZE_WAIT_IN_MS);
expect(this.visModel.invalidateSize).not.toHaveBeenCalled();
jasmine.clock().uninstall();
});
it('should NOT center map to origin when map height is greated than O initially and window is resized', function () {
jasmine.clock().install();
spyOn(this.visModel, 'invalidateSize');
var container = $('<div>').css('height', '800px');
this.visView = createVisView(container, this.visModel, this.settingsModel);
this.visView.render();
// Window is resized by map is not centered since height hasn't changed
$(window).trigger('resize');
jasmine.clock().tick(INVALIDATE_SIZE_WAIT_IN_MS);
expect(this.visModel.invalidateSize).not.toHaveBeenCalled();
});
it('should display/hide the loader while loading', function () {
this.visModel.overlaysCollection.add({ type: 'loader' });
expect(this.visView.$el.find('.CDB-Loader:not(.is-visible)').length).toEqual(1);
this.visModel._engine._eventEmmitter.trigger(Engine.Events.RELOAD_STARTED);
expect(this.visView.$el.find('.CDB-Loader.is-visible').length).toEqual(1);
this.visModel._engine._eventEmmitter.trigger(Engine.Events.RELOAD_SUCCESS);
expect(this.visView.$el.find('.CDB-Loader:not(.is-visible)').length).toEqual(1);
});
});
File diff suppressed because it is too large Load Diff