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: '

{{ name }}

' }; 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(); }); }); });