require 'spec_helper_min' require 'mock_redis' require 'carto/api/vizjson3_presenter' require 'carto/api/vizjson_presenter' describe Carto::Api::VizJSON3Presenter do include Carto::Factories::Visualizations include_context 'visualization creation helpers' before(:all) do @user1 = FactoryGirl.create(:carto_user, private_tables_enabled: true) end after(:all) do @user1.destroy end let(:redis_mock) do MockRedis.new end shared_context 'full visualization' do before(:all) do bypass_named_maps @map, @table, @table_visualization, @visualization = create_full_visualization(Carto::User.find(@user1.id)) @table.update_attribute(:privacy, Carto::UserTable::PRIVACY_PUBLIC) end after(:all) do destroy_full_visualization(@map, @table, @table_visualization, @visualization) end let(:viewer_user) { @visualization.user } end describe 'caching' do include_context 'full visualization' let(:fake_vizjson) { { fake: 'sure!', layers: [] } } it 'to_vizjson uses the redis vizjson cache' do cache_mock = mock cache_mock.expects(:cached).with(@visualization.id, false, 3).twice.returns(fake_vizjson) presenter = Carto::Api::VizJSON3Presenter.new(@visualization, cache_mock) v1 = presenter.to_vizjson v2 = presenter.to_vizjson v1.should eq fake_vizjson v1.should eq v2 end it 'every call to_vizjson uses calculate_vizjson if no cache is provided' do presenter = Carto::Api::VizJSON3Presenter.new(@visualization, nil) presenter.expects(:calculate_vizjson).twice.returns(fake_vizjson) presenter.to_vizjson presenter.to_vizjson end it 'to_vizjson is not overriden by v2 caching or to_named_map_vizjson' do v2_presenter = Carto::Api::VizJSONPresenter.new(@visualization, $tables_metadata) v3_presenter = Carto::Api::VizJSON3Presenter.new(@visualization) v2_vizjson = v2_presenter.to_vizjson v3_vizjson = v3_presenter.to_vizjson v3n_vizjson = v3_presenter.to_named_map_vizjson v3_vizjson.should_not eq v2_vizjson v2_vizjson[:version].should eq '0.1.0' v3_vizjson[:version].should eq '3.0.0' v3n_vizjson[:version].should eq '3.0.0' end it 'to_vizjson does not override v2 caching or named map vizjson' do v2_presenter = Carto::Api::VizJSONPresenter.new(@visualization, $tables_metadata) v3_presenter = Carto::Api::VizJSON3Presenter.new(@visualization) v3_vizjson = v3_presenter.to_vizjson v2_vizjson = v2_presenter.to_vizjson v3n_vizjson = v3_presenter.to_named_map_vizjson v2_vizjson.should_not eq v3_vizjson v3n_vizjson.should_not eq v3_vizjson v2_vizjson[:version].should eq '0.1.0' v3_vizjson[:version].should eq '3.0.0' v3n_vizjson[:version].should eq '3.0.0' end it 'to_named_map_vizjson uses the redis vizjson cache' do fake_vizjson = { fake: 'sure!', layers: [] } cache_mock = mock cache_mock.expects(:cached).with(@visualization.id, false, anything).returns(fake_vizjson).twice presenter = Carto::Api::VizJSON3Presenter.new(@visualization, cache_mock) v1 = presenter.to_named_map_vizjson v2 = presenter.to_named_map_vizjson v1.should eq fake_vizjson v1.should eq v2 end end describe '#to_vizjson (without caching)' do include_context 'full visualization' it 'returns map bounds' do presenter = Carto::Api::VizJSON3Presenter.new(@visualization, nil) presenter.to_vizjson[:bounds].should eq [[-85.0511, -179], [85.0511, 179]] end it 'returns nil map bounds if map bounds are not set' do @visualization.map.view_bounds_sw = nil @visualization.map.view_bounds_ne = nil Carto::Api::VizJSON3Presenter.new(@visualization, nil).to_vizjson[:bounds].should be_nil end end describe '#to_named_map_vizjson' do include_context 'full visualization' it 'generates the vizjson of visualizations that have not named map as if they had' do @table.privacy = Carto::UserTable::PRIVACY_PUBLIC @table.save @visualization = Carto::Visualization.find(@visualization.id) v3_presenter = Carto::Api::VizJSON3Presenter.new(@visualization, nil) original_vizjson = v3_presenter.to_vizjson.reject { |k, _| k == :updated_at } original_named_vizjson = v3_presenter.to_named_map_vizjson.reject { |k, _| k == :updated_at } original_vizjson.should_not eq original_named_vizjson @table.privacy = Carto::UserTable::PRIVACY_PRIVATE @table.save! @visualization = Carto::Visualization.find(@visualization.id) v3_presenter = Carto::Api::VizJSON3Presenter.new(@visualization, nil) named_vizjson = v3_presenter.to_vizjson.reject { |k, _| k == :updated_at } original_named_vizjson.should eq named_vizjson named_named_vizjson = v3_presenter.to_vizjson.reject { |k, _| k == :updated_at } named_named_vizjson.should eq named_vizjson end it 'includes analyses information without including sources parameters' do analysis = FactoryGirl.create(:analysis_with_source, visualization: @visualization, user: @user1) analysis.analysis_definition[:params].should_not be_nil @visualization.reload v3_presenter = Carto::Api::VizJSON3Presenter.new(@visualization, nil) named_vizjson = v3_presenter.to_vizjson analyses_json = named_vizjson[:analyses] analyses_json.should_not be_nil source_analysis_definition = analyses_json[0][:params][:source] source_analysis_definition[:type].should eq 'source' source_analysis_definition[:params].should be_nil end it 'allows whitespace layer names' do layer = @visualization.data_layers.first layer.options['table_name_alias'] = ' ' layer.save @visualization.reload v3_vizjson = Carto::Api::VizJSON3Presenter.new(@visualization, viewer_user).send :calculate_vizjson v3_vizjson[:layers][1][:options][:layer_name].should eq ' ' end it 'includes source at layers options' do source = 'a1' layer = @visualization.data_layers.first layer.options['source'] = source layer.save @table.privacy = Carto::UserTable::PRIVACY_PRIVATE @table.save! @visualization.reload v3_vizjson = Carto::Api::VizJSON3Presenter.new(@visualization, viewer_user).send :calculate_vizjson v3_vizjson[:layers][1][:options][:source].should eq source end it 'includes cartocss at layers options' do cartocss = '#layer { marker-fill: #fabada; }' layer = @visualization.data_layers.first layer.options['tile_style'] = cartocss layer.save @table.privacy = Carto::UserTable::PRIVACY_PRIVATE @table.save! @visualization.reload v3_vizjson = Carto::Api::VizJSON3Presenter.new(@visualization, viewer_user).send :calculate_vizjson v3_vizjson[:layers][1][:options][:cartocss].should eq cartocss end end describe 'analyses' do include_context 'full visualization' it 'sends `source` at layer options instead of sql if source is set for named maps' do query = "select * from #{@table.name}" layer = @visualization.data_layers.first layer.options['source'].should eq nil layer.options['query'] = query layer.save # INFO: send :calculate_vizjson won't use cache v2_vizjson = Carto::Api::VizJSONPresenter.new(@visualization, $tables_metadata).send(:calculate_vizjson) nm_vizjson = Carto::Api::VizJSONPresenter.new(@visualization, $tables_metadata).send(:calculate_vizjson, for_named_map: true) v3_vizjson = Carto::Api::VizJSON3Presenter.new(@visualization, viewer_user).send(:calculate_vizjson) v2_vizjson[:layers][1][:options][:layer_definition][:layers][0][:options][:sql].should eq query v2_vizjson[:layers][1][:options][:layer_definition][:layers][0][:options][:source].should be_nil nm_vizjson[:layers][1][:options][:layer_definition][:layers][0][:options][:sql].should eq query nm_vizjson[:layers][1][:options][:layer_definition][:layers][0][:options][:source].should be_nil v3_vizjson[:layers][1][:options][:sql].should eq query v3_vizjson[:layers][1][:options][:source].should be_nil source = 'a1' layer.options['source'] = source layer.save @visualization.reload v2_vizjson = Carto::Api::VizJSONPresenter.new(@visualization, $tables_metadata).send(:calculate_vizjson) nm_vizjson = Carto::Api::VizJSONPresenter.new(@visualization, $tables_metadata).send(:calculate_vizjson, for_named_map: true) v3_vizjson = Carto::Api::VizJSON3Presenter.new(@visualization, viewer_user).send(:calculate_vizjson) v2_vizjson[:layers][1][:options][:layer_definition][:layers][0][:options][:sql].should eq query v2_vizjson[:layers][1][:options][:layer_definition][:layers][0][:options][:source].should be_nil nm_vizjson[:layers][1][:options][:layer_definition][:layers][0][:options][:sql].should be_nil nm_vizjson[:layers][1][:options][:layer_definition][:layers][0][:options][:source].should eq(id: source) v3_vizjson[:layers][1][:options][:sql].should be_nil v3_vizjson[:layers][1][:options][:source].should eq source end end describe 'anonymous_vizjson' do include_context 'full visualization' it 'v3 should include sql_wrap' do v3_vizjson = Carto::Api::VizJSON3Presenter.new(@visualization, viewer_user).send(:calculate_vizjson) v3_vizjson[:layers][1][:options][:sql_wrap].should eq "select * from (<%= sql %>) __wrap" end end describe 'layers' do include_context 'full visualization' before(:all) do @data_layer = @map.data_layers.first @data_layer.options[:attribution] = 'CARTO attribution' @data_layer.save @torque_layer = FactoryGirl.create(:carto_layer, kind: 'torque', maps: [@map]) @torque_layer.options[:table_name] = 'wadus' @torque_layer.save @visualization.reload end shared_examples 'common layer checks' do it 'should not include layergroup layers' do vizjson[:layers].map { |l| l[:type] }.should_not include 'layergroup' end it 'should not include namedmap layers' do vizjson[:layers].map { |l| l[:type] }.should_not include 'namedmap' end it 'should have exactly three layers: tiled, CartoDB and torque' do vizjson[:layers].map { |l| l[:type] }.should eq %w(tiled CartoDB torque) end it 'should include attribution for all layers' do vizjson[:layers].each { |l| l[:options].should include :attribution } end it 'should not include named map options in any layer' do vizjson[:layers].each do |l| options = l[:options] options.should_not include :stat_tag options.should_not include :maps_api_template options.should_not include :sql_api_template options.should_not include :named_map end end it 'should not include interactivity in any layer' do vizjson[:layers].each do |l| l.should_not include :interactivity l[:options].should_not include :interactivity end end it 'should not include infowindow nor tooltip in basemaps' do vizjson[:layers].each do |l| if l[:type] == 'tiled' l.should_not include :infowindow l.should_not include :tooltip end end end it 'should not include order in any layer' do vizjson[:layers].each do |l| l.should_not include :order end end it 'should include layer_name in options for data layers' do vizjson[:layers].each do |layer| unless layer[:type] == 'tiled' layer.should_not include :layer_name layer[:options][:layer_name].should be end end end it 'should not include Odyssey options' do vizjson.should_not include :prev vizjson.should_not include :next vizjson.should_not include :transition_options end end describe 'in namedmap vizjson' do let(:vizjson) do Carto::Api::VizJSON3Presenter.new(@visualization, viewer_user) .send(:calculate_vizjson, forced_privacy_version: :force_named) end include_examples 'common layer checks' it 'should not include sql field in data layers' do data_layer_options = vizjson[:layers][1][:options] data_layer_options.should_not include :sql end it 'should include cartocss but not sql in torque layers' do torque_layer = vizjson[:layers][2] torque_layer.should_not include :sql torque_layer[:cartocss].should be torque_layer[:cartocss_version].should be end end describe 'in anonymous vizjson' do let(:vizjson) do Carto::Api::VizJSON3Presenter.new(@visualization, viewer_user) .send(:calculate_vizjson, forced_privacy_version: :force_anonymous) end include_examples 'common layer checks' it 'should include sql and cartocss fields in data layers' do data_layer_options = vizjson[:layers][1][:options] data_layer_options[:sql].should be data_layer_options[:cartocss].should be data_layer_options[:cartocss_version].should be end it 'should include sql and cartocss fields in torque layers' do torque_layer = vizjson[:layers][2] torque_layer[:sql].should be torque_layer[:cartocss].should be torque_layer[:cartocss_version].should be end end describe 'overlays' do include_context 'full visualization' def vizjson Carto::Api::VizJSON3Presenter.new(@visualization, viewer_user) .send(:calculate_vizjson, forced_privacy_version: :force_anonymous) end it 'enables map layer_selector option if there is a layer_selector overlay' do vizjson[:options]['layer_selector'].should eq false @visualization.overlays << Carto::Overlay.new(type: 'layer_selector') vizjson[:options]['layer_selector'].should eq true end it 'removes layer selector overlay ' do @visualization.overlays << Carto::Overlay.new(type: 'layer_selector') vizjson[:overlays].any? { |o| o[:type] == 'layer_selector' }.should be_false end end end end