var $ = require('jquery'); var _ = require('underscore'); var Vis = require('../../../src/vis/vis'); var VizJSON = require('../../../src/api/vizjson'); var DataviewModelBase = require('../../../src/dataviews/dataview-model-base'); var AnalysisModel = require('../../../src/analysis/analysis-model'); var AnalysisService = require('../../../src/analysis/analysis-service'); var LayersFactory = require('../../../src/vis/layers-factory'); var fakeVizJSON = function () { return { 'id': '03a89434-379e-11e6-b2e3-0e674067d321', 'version': '3.0.0', 'title': 'Untitled Map 6', 'likes': 0, 'description': null, 'scrollwheel': false, 'legends': true, 'map_provider': 'leaflet', 'bounds': [ [ -37.814, -123.11934 ], [ 64, 153.02809 ] ], 'center': '[13.093,14.954374999999999]', 'zoom': 1, 'updated_at': '2016-06-22T14:25:00+00:00', 'layers': [ { 'options': { 'default': 'true', 'url': 'http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png', 'subdomains': 'abcd', 'minZoom': '0', 'maxZoom': '18', 'name': 'Positron', 'className': 'positron_rainbow_labels', 'attribution': '© OpenStreetMap contributors © CARTO', 'labels': { 'url': 'http://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}.png' }, 'urlTemplate': 'http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png' }, 'infowindow': null, 'tooltip': null, 'id': 'bd329548-dfdf-4d05-95f5-4773d041b91d', 'order': 0, 'type': 'tiled' }, { 'id': 'aefb9966-e587-40dc-8be7-6e85949f8ebe', 'type': 'CartoDB', 'infowindow': { 'fields': [ { 'name': 'gnip', 'title': true, 'position': 0 }, { 'name': 'actor_disp', 'title': true, 'position': 1 } ], 'template_name': 'table/views/infowindow_light', 'template': '
\n
\n
\n
\n
\n {{#loading}}\n
\n {{/loading}}\n
    \n {{#content.fields}}\n
  • \n {{#title}}
    {{title}}
    {{/title}}\n {{#value}}

    {{{ value }}}

    {{/value}}\n {{^value}}

    null

    {{/value}}\n
  • \n {{/content.fields}}\n
\n
\n
\n
\n
\n
\n
\n
\n', 'alternative_names': {}, 'width': 226, 'maxHeight': 180 }, 'tooltip': { 'fields': [], 'template_name': 'tooltip_light', 'template': '
\n \n
\n', 'alternative_names': {}, 'maxHeight': 180 }, 'legend': { 'type': 'none', 'show_title': false, 'title': '', 'template': '', 'visible': true }, 'order': 1, 'visible': true, 'options': { 'layer_name': 'twitter_t3chfest_reduced', 'cartocss': '/** simple visualization */\n\n#twitter_t3chfest_reduced{\n marker-fill-opacity: 0.9;\n marker-line-color: #FFF;\n marker-line-width: 1;\n marker-line-opacity: 1;\n marker-placement: point;\n marker-type: ellipse;\n marker-width: 10;\n marker-fill: #FF6600;\n marker-allow-overlap: true;\n}', 'cartocss_version': '2.1.1', 'interactivity': 'cartodb_id', 'source': 'a0' } }, { 'options': { 'default': 'true', 'url': 'http://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}.png', 'subdomains': 'abcd', 'minZoom': '0', 'maxZoom': '18', 'attribution': '© OpenStreetMap contributors © CARTO', 'urlTemplate': 'http://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}.png', 'type': 'Tiled', 'name': 'Positron Labels' }, 'infowindow': null, 'tooltip': null, 'id': '2a0b6f3a-e11c-4ff8-b3d3-65366935341a', 'order': 2, 'type': 'tiled' } ], 'overlays': [ { 'type': 'share', 'order': 2, 'options': { 'display': true, 'x': 20, 'y': 20 }, 'template': '' }, { 'type': 'search', 'order': 3, 'options': { 'display': true, 'x': 60, 'y': 20 }, 'template': '' }, { 'type': 'zoom', 'order': 6, 'options': { 'display': true, 'x': 20, 'y': 20 }, 'template': '+ -' }, { 'type': 'loader', 'order': 8, 'options': { 'display': true, 'x': 20, 'y': 150 }, 'template': '
' }, { 'type': 'logo', 'order': 9, 'options': { 'display': true, 'x': 10, 'y': 40 }, 'template': '' } ], 'prev': null, 'next': null, 'transition_options': { 'time': 0 }, 'widgets': [], 'datasource': { 'user_name': 'cartojs-test', 'maps_api_template': 'https://{user}.carto.com:443', 'stat_tag': '03a89434-379e-11e6-b2e3-0e674067d321' }, 'user': { 'fullname': 'Pablo', 'avatar_url': '//cartodb-libs.global.ssl.fastly.net/cartodbui/assets/unversioned/images/avatars/avatar_planet_green.png' }, 'analyses': [ { 'id': 'a0', 'type': 'source', 'params': { 'query': 'SELECT * FROM twitter_t3chfest_reduced limit 1' }, 'options': { 'table_name': 'twitter_t3chfest_reduced' } } ], 'vector': false }; }; describe('vis/vis', function () { beforeEach(function () { spyOn(Vis.prototype, 'reload').and.callThrough(); this.vis = new Vis(); }); it('.trackLoadingObject .untrackLoadingObject and .clearLoadingObjects should change the loading attribute', function () { var object = { id: 'id' }; // 'loading' is false by default expect(this.vis.get('loading')).toEqual(false); this.vis.trackLoadingObject(object); // an object is being loaded so 'loading' is true expect(this.vis.get('loading')).toEqual(true); this.vis.untrackLoadingObject(object); // no objects are being loaded again so 'loading' is false expect(this.vis.get('loading')).toEqual(false); }); describe('bindings to collection of layers', function () { beforeEach(function () { Vis.prototype.reload.and.callFake(function (options) { options && options.success && options.success(); }); this.vis.load(new VizJSON(fakeVizJSON()), {}); this.vis.instantiateMap(); Vis.prototype.reload.calls.reset(); }); it('should reload the map when layers are resetted', function () { this.vis.map.layers.reset([{ id: 'layer1' }]); expect(Vis.prototype.reload).toHaveBeenCalled(); }); describe('on added layer', function () { it('should reload the map', function () { this.vis.map.layers.add({ id: 'layer1' }); expect(Vis.prototype.reload).toHaveBeenCalledWith({ sourceId: 'layer1' }); }); it('should not reload the map if silent param is true', function () { this.vis.map.layers.add({ id: 'layer1' }, { silent: true }); expect(Vis.prototype.reload).not.toHaveBeenCalled(); }); }); describe('on removed layer', function () { var layer; beforeEach(function () { layer = this.vis.map.layers.add({ id: 'layer1', type: 'CartoDB' }, { silent: true }); }); it('should reload the map', function () { this.vis.map.layers.remove(layer); expect(Vis.prototype.reload).toHaveBeenCalledWith({ sourceId: 'layer1' }); }); it('should not reload the map if silent param is true', function () { this.vis.map.layers.remove(layer, { silent: true }); expect(Vis.prototype.reload).not.toHaveBeenCalled(); }); }); it('should reload the map when layers are moved', function () { this.vis.map.layers.add({ id: 'layer1', type: 'Tiled' }, { silent: true }); this.vis.map.layers.add({ id: 'layer2', type: 'CartoDB' }, { silent: true }); Vis.prototype.reload.calls.reset(); this.vis.map.moveCartoDBLayer(1, 0); expect(Vis.prototype.reload.calls.count()).toBe(1); }); }); describe('.reload', function () { beforeEach(function () { this.vis.load(new VizJSON(fakeVizJSON()), {}); }); describe("when vis hasn't been instantiated yet", function () { it('should NOT instantiate map', function (done) { spyOn(this.vis._engine, 'reload'); this.vis.reload({}).then(() => { expect(this.vis._engine.reload).not.toHaveBeenCalled(); done(); }); }); }); describe('when vis has been instantiated once', function () { beforeEach(function () { this.vis._instantiateMapWasCalled = true; }); it('should instantiate map and forward options', function () { spyOn(this.vis._engine, 'reload'); this.vis.reload({ a: 1, b: 2, sourceId: 'sourceIdMock', forceFetch: 'forceFetchMock' }); expect(this.vis._engine.reload).toHaveBeenCalledWith({ a: 1, b: 2, sourceId: 'sourceIdMock', forceFetch: 'forceFetchMock' }); }); it('should execute the success callback if the reload succeeds', function (done) { var successSpy = jasmine.createSpy('sucessCallback'); // Mock the server request. spyOn(this.vis._engine._windshaftClient, 'instantiateMap').and.callFake(function (request) { request.options.success({ metadata: {} }); }); this.vis.reload({ a: 1, b: 2, sourceId: 'sourceIdMock', forceFetch: 'forceFetchMock', success: successSpy }).then(function () { expect(successSpy).toHaveBeenCalled(); done(); }); }); it('should execute the error callback if the reload error', function (done) { var errorSpy = jasmine.createSpy('errorCallback'); // Mock the server request. spyOn(this.vis._engine._windshaftClient, 'instantiateMap').and.callFake(function (request) { request.options.error(); }); this.vis.reload({ a: 1, b: 2, sourceId: 'sourceIdMock', forceFetch: 'forceFetchMock', error: errorSpy }).catch(function () { expect(errorSpy).toHaveBeenCalled(); done(); }); }); it('should trigger a `reload` event', function () { var reloadCallback = jasmine.createSpy('reloadCallback'); this.vis.on('reload', reloadCallback); this.vis.reload(); expect(reloadCallback).toHaveBeenCalled(); }); it('should trigger a `reloaded` event when reload succeeds', function (done) { var onReloadedSpy = jasmine.createSpy('onReloadedSpy'); this.vis.on('reloaded', onReloadedSpy); // Mock the server request. // TODO: Mock $.ajax spyOn(this.vis._engine._windshaftClient, 'instantiateMap').and.callFake(function (request) { request.options.success({ metadata: {} }); }); this.vis.reload({}).then(function () { expect(onReloadedSpy).toHaveBeenCalled(); done(); }); }); }); }); describe('.instantiateMap', function () { beforeEach(function () { this.vis.load(new VizJSON(fakeVizJSON()), {}); Vis.prototype.reload.calls.reset(); }); it('should instantiate without filters if no filters', function () { this.vis._isAnyDataviewFiltered = function () { return false; }; Vis.prototype.reload.calls.reset(); this.vis.instantiateMap(); expect(Vis.prototype.reload).toHaveBeenCalled(); expect(Vis.prototype.reload.calls.mostRecent().args[0].includeFilters).toBe(false); // include filters }); it('should instantiate twice if filters', function () { this.vis._isAnyDataviewFiltered = function () { return true; }; Vis.prototype.reload.calls.reset(); this.vis.instantiateMap(); Vis.prototype.reload.calls.mostRecent().args[0].success(); expect(Vis.prototype.reload).toHaveBeenCalledTimes(2); expect(Vis.prototype.reload.calls.mostRecent().args[0].includeFilters).toBe(true); // include filters }); }); describe('.load', function () { beforeEach(function () { this.vis.load(new VizJSON(fakeVizJSON()), {}); }); it('should correctly set some public properties', function () { expect(this.vis.map).toBeDefined(); expect(this.vis.analysis).toBeDefined(); expect(this.vis.dataviews).toBeDefined(); expect(this.vis.layerGroupModel).toBeDefined(); expect(this.vis.overlaysCollection).toBeDefined(); }); it('should load the layers', function () { expect(this.vis.map.layers.size()).toEqual(3); }); it('should assign the analysis source', function () { var cartoDBLayerSource = this.vis.map.layers.at(1).getSource(); expect(cartoDBLayerSource instanceof AnalysisModel).toBe(true); expect(cartoDBLayerSource.id).toEqual('a0'); }); it('should create a "source" analysis if sql option is present', function () { var vizjson = fakeVizJSON(); // CartoDB layer as sql option instead of source vizjson.layers[1].options.id = 'LAYER_ID'; vizjson.layers[1].options.sql = 'SELECT foo FROM bar'; delete vizjson.layers[1].options.source; this.vis.load(new VizJSON(vizjson, {})); var analysisNode = this.vis.analysis.findNodeById('LAYER_ID'); expect(analysisNode).toBeDefined(); expect(analysisNode.get('id')).toEqual('LAYER_ID'); }); it('should use the given provider', function () { var vizjson = fakeVizJSON(); vizjson.map_provider = 'googlemaps'; this.vis.load(new VizJSON(vizjson)); expect(this.vis.map.get('provider')).toEqual('googlemaps'); }); describe('dragging option', function () { beforeEach(function () { this.vizjson = { 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]], scrollwheel: true, overlays: [], 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' } }; }); it('should be enabled with zoom overlay and scrollwheel enabled', function () { this.vizjson.overlays = [ { type: 'zoom', order: 6, options: { x: 20, y: 20, display: true }, template: '' } ]; this.vis.load(new VizJSON(this.vizjson)); expect(this.vis.map.get('drag')).toBeTruthy(); }); it('should be enabled with zoom overlay and scrollwheel disabled', function () { this.vizjson.overlays = [ { type: 'zoom', order: 6, options: { x: 20, y: 20, display: true }, template: '' } ]; this.vis.load(new VizJSON(this.vizjson)); expect(this.vis.map.get('drag')).toBeTruthy(); }); it('should be enabled without zoom overlay and scrollwheel enabled', function () { this.vizjson.scrollwheel = true; this.vis.load(new VizJSON(this.vizjson)); expect(this.vis.map.get('drag')).toBeTruthy(); }); it('should be disabled without zoom overlay and scrollwheel disabled', function () { this.vizjson.scrollwheel = false; this.vis.load(new VizJSON(this.vizjson)); expect(this.vis.map.get('drag')).toBeFalsy(); }); }); it('when https is false all the urls should be transformed to http', function () { var vizjson = fakeVizJSON(); vizjson.layers = [{ type: 'tiled', options: { urlTemplate: 'https://dnv9my2eseobd.cloudfront.net/v3/{z}/{x}/{y}.png' } }]; spyOn(LayersFactory, 'isHttps').and.returnValue(false); this.vis.load(new VizJSON(vizjson)); expect(this.vis.map.layers.at(0).get('urlTemplate')).toEqual( 'http://a.tiles.mapbox.com/v3/{z}/{x}/{y}.png' ); }); it('when https is true all urls should NOT be transformed to http', function () { var vizjson = fakeVizJSON(); vizjson.layers = [{ type: 'tiled', options: { urlTemplate: 'https://dnv9my2eseobd.cloudfront.net/v3/{z}/{x}/{y}.png' } }]; spyOn(LayersFactory, 'isHttps').and.returnValue(true); this.vis.load(new VizJSON(vizjson)); expect(this.vis.map.layers.at(0).get('urlTemplate')).toEqual( 'https://dnv9my2eseobd.cloudfront.net/v3/{z}/{x}/{y}.png' ); }); it('should initialize existing analyses', function () { this.vizjson = { bounds: [[40.2813, -3.90592], [40.5611, -3.47532]], center: [40.421217, -3.6756585], layers: [ { type: 'CartoDB', options: { source: 'a1', cartocss: 'cartocss' } } ], 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' }, analyses: [ { id: 'a1', type: 'buffer', params: { source: { id: 'a0', type: 'source', params: { 'query': 'SELECT * FROM airbnb_listings' } }, radio: 300 } } ] }; this.vis.load(new VizJSON(this.vizjson)); // Analyses have been indexed var analysisNodes = AnalysisService.getUniqueAnalysisNodes(this.vis._layersCollection, this.vis._dataviewsCollection); expect(analysisNodes.length).toEqual(2); var a1 = this.vis.analysis.findNodeById('a1'); var a0 = this.vis.analysis.findNodeById('a0'); // Analysis graph has been created correctly expect(a1.get('source')).toEqual(a0); }); }); describe('polling', function () { beforeEach(function () { spyOn(_, 'debounce').and.callFake(function (func) { return function () { func.apply(this, arguments); }; }); this.vizjson = { 'id': '70af2a72-0709-11e6-a834-080027880ca6', 'version': '3.0.0', 'title': 'Untitled Map 1', 'likes': 0, 'description': null, 'scrollwheel': false, 'legends': true, 'map_provider': 'leaflet', 'bounds': [ [ 41.358088, 2.089675 ], [ 41.448257, 2.215129 ] ], 'center': '[41.4031725,2.1524020000000004]', 'zoom': 11, 'updated_at': '2016-04-20T17:05:02+00:00', 'layers': [ { 'id': 'e0d06945-74cd-4421-8229-561c3cabc854', 'type': 'CartoDB', 'infowindow': { 'fields': [], 'template_name': 'table/views/infowindow_light', 'template': '
\n
\n
\n
\n
    \n {{#loading}}\n
    \n {{/loading}}\n {{#content.fields}}\n
  • \n {{#title}}
    {{title}}
    {{/title}}\n {{#value}}

    {{{ value }}}

    {{/value}}\n {{^value}}

    null

    {{/value}}\n
  • \n {{/content.fields}}\n
\n
\n
\n
\n
\n
\n
\n
\n', 'alternative_names': {}, 'width': 226, 'maxHeight': 180 }, 'tooltip': { 'fields': [], 'template_name': 'tooltip_light', 'template': '
\n \n
\n', 'alternative_names': {}, 'maxHeight': 180 }, 'legends': [ { type: 'bubble', title: 'My Bubble Legend' } ], 'order': 1, 'visible': true, 'options': { 'layer_name': 'arboles', 'cartocss': 'cartocss', 'cartocss_version': '2.1.1', 'interactivity': 'cartodb_id', 'source': 'a1' } } ], 'overlays': [], 'widgets': [], 'datasource': { 'user_name': 'cdb', 'maps_api_template': 'http://{user}.localhost.lan:8181', 'stat_tag': '70af2a72-0709-11e6-a834-080027880ca6' }, 'user': { 'fullname': 'cdb', 'avatar_url': '//example.com/avatars/avatar_stars_blue.png' }, 'analyses': [ { 'id': 'a1', 'type': 'trade-area', 'params': { 'source': { 'id': 'a0', 'type': 'source', 'params': { 'query': 'SELECT * FROM arboles' } }, 'kind': 'drive', 'time': 10 } } ], 'vector': false }; }); it('should start polling', function () { // Mock server response spyOn($, 'ajax').and.callFake(function (params) { var fakeResponse = { 'layergroupid': '9d7bf465e45113123bf9949c2a4f0395:0', 'metadata': { 'layers': [ { 'type': 'mapnik', 'meta': { 'stats': [], 'cartocss': 'cartocss' } } ], 'dataviews': {}, 'analyses': [ { 'nodes': { 'a0': { 'status': 'ready', 'query': 'SELECT * FROM arboles', 'url': { 'http': 'http://cdb.localhost.lan:8181/api/v1/map/9d7bf465e45113123bf9949c2a4f0395:0/analysis/node/5af683d5d8a6f67e11916a31cd76632884d4064f' } }, 'a1': { 'status': 'pending', 'query': 'select * from analysis_trade_area_e65b1ae05854aea96266808ec0686b91f3ee0a81', 'url': { 'http': 'http://cdb.localhost.lan:8181/api/v1/map/9d7bf465e45113123bf9949c2a4f0395:0/analysis/node/e65b1ae05854aea96266808ec0686b91f3ee0a81' } } } } ] }, 'last_updated': '1970-01-01T00:00:00.000Z' }; params.success(fakeResponse); }); this.vis.load(new VizJSON(this.vizjson)); // Skip reload debouncer this.vis._engine.reload = this.vis._engine._performReload; this.vis.instantiateMap(); // Polling has started expect($.ajax.calls.argsFor(1)[0].url).toEqual('http://cdb.localhost.lan:8181/api/v1/map/9d7bf465e45113123bf9949c2a4f0395:0/analysis/node/e65b1ae05854aea96266808ec0686b91f3ee0a81'); }); }); describe('loading state when vis is reloaded', function () { beforeEach(function () { spyOn(_, 'debounce').and.callFake(function (func) { return function () { func.apply(this, arguments); }; }); this.vizjson = { 'id': '70af2a72-0709-11e6-a834-080027880ca6', 'version': '3.0.0', 'title': 'Untitled Map 1', 'likes': 0, 'description': null, 'scrollwheel': false, 'legends': true, 'map_provider': 'leaflet', 'bounds': [ [ 41.358088, 2.089675 ], [ 41.448257, 2.215129 ] ], 'center': '[41.4031725,2.1524020000000004]', 'zoom': 11, 'updated_at': '2016-04-20T17:05:02+00:00', 'layers': [ { 'id': 'e0d06945-74cd-4421-8229-561c3cabc854', 'type': 'CartoDB', 'infowindow': { 'fields': [], 'template_name': 'table/views/infowindow_light', 'template': '
\n
\n
\n
\n
    \n {{#loading}}\n
    \n {{/loading}}\n {{#content.fields}}\n
  • \n {{#title}}
    {{title}}
    {{/title}}\n {{#value}}

    {{{ value }}}

    {{/value}}\n {{^value}}

    null

    {{/value}}\n
  • \n {{/content.fields}}\n
\n
\n
\n
\n
\n
\n
\n
\n', 'alternative_names': {}, 'width': 226, 'maxHeight': 180 }, 'tooltip': { 'fields': [], 'template_name': 'tooltip_light', 'template': '
\n \n
\n', 'alternative_names': {}, 'maxHeight': 180 }, 'legends': [ { type: 'bubble', title: 'My Bubble Legend' } ], 'order': 1, 'visible': true, 'options': { 'layer_name': 'arboles', 'cartocss': 'cartocss', 'cartocss_version': '2.1.1', 'interactivity': 'cartodb_id', 'source': 'a1' } } ], 'overlays': [], 'widgets': [], 'datasource': { 'user_name': 'cdb', 'maps_api_template': 'http://{user}.localhost.lan:8181', 'stat_tag': '70af2a72-0709-11e6-a834-080027880ca6' }, 'user': { 'fullname': 'cdb', 'avatar_url': '//example.com/avatars/avatar_stars_blue.png' }, 'analyses': [ { 'id': 'a1', 'type': 'trade-area', 'params': { 'source': { 'id': 'a0', 'type': 'source', 'params': { 'query': 'SELECT * FROM arboles' } }, 'kind': 'drive', 'time': 10 } } ], 'vector': false }; spyOn($, 'ajax'); this.vis.load(new VizJSON(this.vizjson)); // Skip reload debouncer this.vis._engine.reload = this.vis._engine._performReload; this.vis.instantiateMap(); }); it('should mark vis as loading if an analysis is not done', function () { expect(this.vis.isLoading()).toBe(false); // Response from Maps API is received -> ALL ANALYSES READY $.ajax.calls.argsFor(0)[0].success({ 'layergroupid': '9d7bf465e45113123bf9949c2a4f0395:0', 'metadata': { 'layers': [ { 'type': 'mapnik', 'meta': { 'stats': [], 'cartocss': 'cartocss' } } ], 'dataviews': {}, 'analyses': [ { 'nodes': { 'a0': { 'status': 'ready', 'query': 'SELECT * FROM arboles', 'url': { 'http': 'http://cdb.localhost.lan:8181/api/v1/map/9d7bf465e45113123bf9949c2a4f0395:0/analysis/node/5af683d5d8a6f67e11916a31cd76632884d4064f' } }, 'a1': { 'status': 'ready', 'query': 'select * from analysis_trade_area_e65b1ae05854aea96266808ec0686b91f3ee0a81', 'url': { 'http': 'http://cdb.localhost.lan:8181/api/v1/map/9d7bf465e45113123bf9949c2a4f0395:0/analysis/node/e65b1ae05854aea96266808ec0686b91f3ee0a81' } } } } ] }, 'last_updated': '1970-01-01T00:00:00.000Z' }); expect(this.vis.isLoading()).toBe(false); }); it('should NOT mark vis as loading if all analyses are done', function () { expect(this.vis.isLoading()).toBe(false); // Response from Maps API is received -> ONE ANALYSIS PENDING $.ajax.calls.argsFor(0)[0].success({ 'layergroupid': '9d7bf465e45113123bf9949c2a4f0395:0', 'metadata': { 'layers': [ { 'type': 'mapnik', 'meta': { 'stats': [], 'cartocss': 'cartocss' } } ], 'dataviews': {}, 'analyses': [ { 'nodes': { 'a0': { 'status': 'ready', 'query': 'SELECT * FROM arboles', 'url': { 'http': 'http://cdb.localhost.lan:8181/api/v1/map/9d7bf465e45113123bf9949c2a4f0395:0/analysis/node/5af683d5d8a6f67e11916a31cd76632884d4064f' } }, 'a1': { 'status': 'pending', 'query': 'select * from analysis_trade_area_e65b1ae05854aea96266808ec0686b91f3ee0a81', 'url': { 'http': 'http://cdb.localhost.lan:8181/api/v1/map/9d7bf465e45113123bf9949c2a4f0395:0/analysis/node/e65b1ae05854aea96266808ec0686b91f3ee0a81' } }, 'a2': { 'status': 'pending', 'query': 'select * from analysis_trade_area_b35b1ae05854aea96266808ec0686b91f3ee0a81', 'url': { 'http': 'http://cdb.localhost.lan:8181/api/v1/map/9d7bf465e45113123bf9949c2a4f0395:0/analysis/node/b75b1ae05854aea96266808ec0686b91f3ee0a81' } } } } ] }, 'last_updated': '1970-01-01T00:00:00.000Z' }); expect(this.vis.isLoading()).toBe(true); }); }); describe('error handling', function () { beforeEach(function () { spyOn(this.vis, 'setOk'); spyOn(this.vis, 'setError'); }); it('should invoke setOk when request succeeds', function () { spyOn($, 'ajax').and.callFake(function (params) { var fakeResponse = { 'layergroupid': '9d7bf465e45113123bf9949c2a4f0395:0', 'metadata': { 'layers': [ { 'type': 'mapnik', 'meta': { 'stats': [], 'cartocss': 'cartocss' } } ], 'dataviews': {}, 'analyses': [ { 'nodes': { 'a0': { 'status': 'ready', 'query': 'SELECT * FROM arboles', 'url': { 'http': 'http://cdb.localhost.lan:8181/api/v1/map/9d7bf465e45113123bf9949c2a4f0395:0/analysis/node/5af683d5d8a6f67e11916a31cd76632884d4064f' } } } } ] }, 'last_updated': '1970-01-01T00:00:00.000Z' }; params.success(fakeResponse); }); this.vis.load(new VizJSON(fakeVizJSON())); // Skip reload debouncer this.vis._engine.reload = this.vis._engine._performReload; this.vis.instantiateMap(); expect(this.vis.setOk).toHaveBeenCalled(); expect(this.vis.setError).not.toHaveBeenCalled(); }); it('should invoke setError when request fails', function () { spyOn($, 'ajax').and.callFake(function (params) { var error = { errors: ['the error message'], errors_with_context: [ { type: 'unknown', message: 'the error message' } ] }; var fakeResponse = { responseText: JSON.stringify(error) }; params.error(fakeResponse); }); this.vis.load(new VizJSON(fakeVizJSON())); // Skip reload debouncer this.vis._engine.reload = this.vis._engine._performReload; this.vis.instantiateMap(); expect(this.vis.setOk).not.toHaveBeenCalled(); expect(this.vis.setError).toHaveBeenCalled(); var errorArgs = this.vis.setError.calls.mostRecent().args[0]; expect(errorArgs).toBeDefined(); expect(errorArgs.message).toEqual('the error message'); }); }); describe('.setOk', function () { it('should unset the error attribute', function () { this.vis.set('error', 'error'); this.vis.setOk(); expect(this.vis.get('error')).toBeUndefined(); }); it('should invoke the done callback only the first time', function () { var callback = jasmine.createSpy('callback'); this.vis.done(callback); this.vis.setOk(); expect(callback).toHaveBeenCalled(); callback.calls.reset(); this.vis.setOk(); expect(callback).not.toHaveBeenCalled(); }); }); describe('.setError', function () { it('should set the error attribute', function () { this.vis.setError('something'); expect(this.vis.get('error')).toEqual('something'); }); it('should invoke the error callback only the first time', function () { var callback = jasmine.createSpy('callback'); this.vis.error(callback); this.vis.setError('something'); expect(callback).toHaveBeenCalled(); callback.calls.reset(); this.vis.setError('something'); expect(callback).not.toHaveBeenCalled(); }); }); describe('when a vizjson has been loaded', function () { var dataviewMock; var source; beforeEach(function () { spyOn(_, 'debounce').and.callFake(function (func) { return function () { func.apply(this, arguments); }; }); this.vis = new Vis(); this.vis.load(new VizJSON(fakeVizJSON())); this.vis.instantiateMap(); Vis.prototype.reload.calls.mostRecent().args[0].success(); source = this.vis.analysis.findNodeById('a0'); dataviewMock = new DataviewModelBase({ source: source }, { map: this.vis.map, engine: this.vis._engine }); dataviewMock.toJSON = jasmine.createSpy('toJSON').and.returnValue('fakeDataviewSerialization'); }); describe('when a dataview is added', function () { it('should reload the map', function () { Vis.prototype.reload.calls.reset(); this.vis._dataviewsCollection.add(dataviewMock); expect(Vis.prototype.reload).toHaveBeenCalled(); }); }); describe('when a dataview is removed', function () { it('should reload the map if there is a filter and it is not empty', function () { dataviewMock.isFiltered = function () { return true; }; this.vis._dataviewsCollection.add(dataviewMock); Vis.prototype.reload.calls.reset(); dataviewMock.remove(); expect(Vis.prototype.reload).toHaveBeenCalledTimes(1); }); it('should not reload the map if there is not a filter', function () { dataviewMock.isFiltered = function () { return false; }; this.vis._dataviewsCollection.add(dataviewMock); Vis.prototype.reload.calls.reset(); dataviewMock.remove(); expect(Vis.prototype.reload).not.toHaveBeenCalled(); }); }); describe('.getLayers', function () { it('should return layers from map', function () { this.vis.load(new VizJSON(fakeVizJSON())); expect(this.vis.getLayers().length).toEqual(3); }); }); describe('.getLayer', function () { it('should return the layer in the given index', function () { var vizjson = fakeVizJSON(); this.vis.load(new VizJSON(vizjson)); expect(this.vis.getLayer(0).get('id')).toEqual(vizjson.layers[0].id); expect(this.vis.getLayer(1).get('id')).toEqual(vizjson.layers[1].id); expect(this.vis.getLayer(2).get('id')).toEqual(vizjson.layers[2].id); }); }); describe('.invalidateSize', function () { it('should just trigger an event', function () { var callback = jasmine.createSpy('callback'); this.vis.bind('invalidateSize', callback); this.vis.invalidateSize(); expect(callback).toHaveBeenCalled(); }); }); describe('.getStaticImageURL', function () { beforeEach(function () { this.vis.layerGroupModel.set('urls', { 'image': 'http://carto.com/image/1234567890/{z}/{lat}/{lng}/{width}/{height}.{format}' }); }); it('should return the URL with default options', function () { var expectedURL = 'http://carto.com/image/1234567890/4/0/0/300/300.png?layer=0,1,2'; expect(this.vis.getStaticImageURL()).toEqual(expectedURL); }); it('should use given options', function () { var expectedURL = 'http://carto.com/image/1234567890/10/180/-180/400/600.jpg?layer=0,1,2'; expect(this.vis.getStaticImageURL({ zoom: 10, lat: 180, lng: -180, width: 400, height: 600, format: 'jpg' })).toEqual(expectedURL); }); }); }); describe('layerGroupModel errors', function () { it('should do things', function () { this.vis.load(new VizJSON(fakeVizJSON()), {}); var called = false; this.vis.map.on('error:limit', function () { called = true; }); this.vis.layerGroupModel.addError({ type: 'limit', message: 'Something about limits' }); expect(called).toBe(true); }); }); });