diff --git a/.circleci/config.yml b/.circleci/config.yml index 008e8c1bea5..0d7242171ea 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,6 +1,6 @@ version: 2.1 orbs: - browser-tools: circleci/browser-tools@1.4.8 + browser-tools: circleci/browser-tools@1.5.1 # Inspired by: # https://github.com/CircleCI-Public/circleci-demo-workflows/blob/workspace-forwarding/.circleci/config.yml diff --git a/test/image/baselines/map_layers.png b/test/image/baselines/map_layers.png index f49b75ca990..75ee5b12958 100644 Binary files a/test/image/baselines/map_layers.png and b/test/image/baselines/map_layers.png differ diff --git a/test/image/baselines/pattern_fgcolor_overlay_fillmode.png b/test/image/baselines/pattern_fgcolor_overlay_fillmode.png index af59862d58a..6c6b5fbb821 100644 Binary files a/test/image/baselines/pattern_fgcolor_overlay_fillmode.png and b/test/image/baselines/pattern_fgcolor_overlay_fillmode.png differ diff --git a/test/image/baselines/sunburst_coffee.png b/test/image/baselines/sunburst_coffee.png index 590edb08ba9..523744ad90d 100644 Binary files a/test/image/baselines/sunburst_coffee.png and b/test/image/baselines/sunburst_coffee.png differ diff --git a/test/image/baselines/treemap_sunburst_marker_colors.png b/test/image/baselines/treemap_sunburst_marker_colors.png index 209427c1b2a..beebd418791 100644 Binary files a/test/image/baselines/treemap_sunburst_marker_colors.png and b/test/image/baselines/treemap_sunburst_marker_colors.png differ diff --git a/test/image/compare_pixels_test.js b/test/image/compare_pixels_test.js index ad47a502173..75aba5fc7af 100644 --- a/test/image/compare_pixels_test.js +++ b/test/image/compare_pixels_test.js @@ -63,15 +63,11 @@ argv._.forEach(function(pattern) { var blacklist = [ 'map_angles', 'map_stamen-style', - 'mapbox_stamen-style', - 'mapbox_custom-style', - 'mapbox_density0-legend', - 'mapbox_osm-style', ]; if(virtualWebgl) { allMockList = allMockList.filter(function(a) { - return a.slice(0, 2) === 'gl' || a.slice(0, 6) === 'mapbox'; + return a.slice(0, 2) === 'gl'; }); } @@ -108,23 +104,12 @@ for(var i = 0; i < allMockList.length; i++) { // skip blacklist if(blacklist.indexOf(mockName) !== -1) continue; - var isMapbox = mockName.substr(0, 7) === 'mapbox_'; - - // We have to skip mapbox since Aug 2024 - // See https://github.com/plotly/plotly.js/issues/7075 - if(isMapbox) continue; - var flakyMap = [ // more flaky 'map_density0-legend', 'map_osm-style', 'map_predefined-styles1', 'map_predefined-styles2', - - 'mapbox_angles', - 'mapbox_layers', - 'mapbox_custom-style', - 'mapbox_geojson-attributes' ].indexOf(mockName) !== -1; var otherFlaky = [ diff --git a/test/image/export_test.js b/test/image/export_test.js index 47a06ef06cd..d5caded09ad 100644 --- a/test/image/export_test.js +++ b/test/image/export_test.js @@ -26,7 +26,6 @@ var DEFAULT_LIST = [ 'image_astronaut_source', 'gl2d_no-clustering2', 'gl3d_surface-heatmap-treemap_transparent-colorscale', - 'mapbox_density-multiple_legend', 'map_density-multiple_legend', 'smith_modes', 'zsmooth_methods', diff --git a/test/image/make_baseline.py b/test/image/make_baseline.py index 8c260fb658a..519335d31cd 100644 --- a/test/image/make_baseline.py +++ b/test/image/make_baseline.py @@ -55,10 +55,6 @@ pio.kaleido.scope.plotlyjs = plotlyjs pio.templates.default = 'none' -_credentials = open(os.path.join(root, 'build', 'credentials.json'), 'r') -pio.kaleido.scope.mapbox_access_token = json.load(_credentials)['MAPBOX_ACCESS_TOKEN'] -_credentials.close() - ALL_MOCKS = [os.path.splitext(a)[0] for a in os.listdir(dirIn) if a.endswith('.json')] ALL_MOCKS.sort() @@ -70,10 +66,6 @@ # unable to generate baselines for the following mocks blacklist = [ 'map_stamen-style', - 'mapbox_density0-legend', - 'mapbox_osm-style', - 'mapbox_stamen-style', # Could pass by setting mapboxAccessToken to a stadiamaps.com token - 'mapbox_custom-style' # Figure out why needed this in https://github.com/plotly/plotly.js/pull/6610 ] allNames = [a for a in allNames if a not in blacklist] diff --git a/test/image/make_exports.py b/test/image/make_exports.py index ddf12072968..ea8a4efe7f1 100644 --- a/test/image/make_exports.py +++ b/test/image/make_exports.py @@ -10,10 +10,6 @@ pio.templates.default = 'none' pio.kaleido.scope.plotlyjs = os.path.join(root, 'build', 'plotly.js') -_credentials = open(os.path.join(root, 'build', 'credentials.json'), 'r') -pio.kaleido.scope.mapbox_access_token = json.load(_credentials)['MAPBOX_ACCESS_TOKEN'] -_credentials.close() - allFormats = ['svg', 'jpg', 'jpeg', 'webp', 'eps', 'pdf'] # 'png' is tested by image-test @@ -27,7 +23,6 @@ 'image_astronaut_source', 'gl2d_no-clustering2', 'gl3d_surface-heatmap-treemap_transparent-colorscale', - 'mapbox_density-multiple_legend', 'map_density-multiple_legend', 'smith_modes', 'zsmooth_methods', diff --git a/test/image/mocks/map_layers.json b/test/image/mocks/map_layers.json index 10a6f3ff6ea..308b0c26c09 100644 --- a/test/image/mocks/map_layers.json +++ b/test/image/mocks/map_layers.json @@ -189,7 +189,7 @@ { "sourcetype": "raster", "source": [ - "https://img.nj.gov/imagerywms/Natural2015?bbox={bbox-epsg-3857}&format=image/png&service=WMS&version=1.1.1&request=GetMap&srs=EPSG:3857&transparent=true&width=256&height=256&layers=Natural2015" + "https://basemap.nationalmap.gov/arcgis/services/USGSImageryOnly/MapServer/WMSServer?bbox={bbox-epsg-3857}&format=image/png&service=WMS&request=GetMap&crs=EPSG:3857&transparent=true&width=256&height=256&version=1.3.0&layers=0&styles=default" ], "below": "aeroway-line" }, diff --git a/test/jasmine/assets/mock_lists.js b/test/jasmine/assets/mock_lists.js index 05375108c8d..bf93191c0c3 100644 --- a/test/jasmine/assets/mock_lists.js +++ b/test/jasmine/assets/mock_lists.js @@ -68,12 +68,6 @@ var glMockList = [ ['gl3d_volume_multiple-traces', require('../../image/mocks/gl3d_volume_multiple-traces.json')] ]; -var mapboxMockList = [ - ['scattermapbox', require('../../image/mocks/mapbox_bubbles-text.json')], - ['choroplethmapbox', require('../../image/mocks/mapbox_choropleth0.json')], - ['densitymapbox', require('../../image/mocks/mapbox_density0.json')] -]; - var mapMockList = [ ['scattermap', require('../../image/mocks/map_bubbles-text.json')], ['choroplethmap', require('../../image/mocks/map_choropleth0.json')], @@ -83,7 +77,6 @@ var mapMockList = [ module.exports = { svg: svgMockList, gl: glMockList, - mapbox: mapboxMockList, map: mapMockList, - all: svgMockList.concat(glMockList).concat(mapboxMockList).concat(mapMockList) + all: svgMockList.concat(glMockList).concat(mapMockList) }; diff --git a/test/jasmine/tests/choroplethmapbox_test.js b/test/jasmine/tests/choroplethmapbox_test.js deleted file mode 100644 index c9133894265..00000000000 --- a/test/jasmine/tests/choroplethmapbox_test.js +++ /dev/null @@ -1,794 +0,0 @@ -var Plotly = require('../../../lib/index'); -var Plots = require('../../../src/plots/plots'); -var Lib = require('../../../src/lib'); -var loggers = require('../../../src/lib/loggers'); - -var convertModule = require('../../../src/traces/choroplethmapbox/convert'); -var MAPBOX_ACCESS_TOKEN = require('../../../build/credentials.json').MAPBOX_ACCESS_TOKEN; - -var createGraphDiv = require('../assets/create_graph_div'); -var destroyGraphDiv = require('../assets/destroy_graph_div'); -var failTest = require('../assets/fail_test'); -var mouseEvent = require('../assets/mouse_event'); -var supplyAllDefaults = require('../assets/supply_defaults'); - -var assertHoverLabelContent = require('../assets/custom_assertions').assertHoverLabelContent; - -describe('Test choroplethmapbox defaults:', function() { - var gd; - var fullData; - - function _supply(opts, layout) { - gd = {}; - opts = Array.isArray(opts) ? opts : [opts]; - - gd.data = opts.map(function(o) { - return Lib.extendFlat({type: 'choroplethmapbox'}, o || {}); - }); - gd.layout = layout || {}; - - supplyAllDefaults(gd); - fullData = gd._fullData; - } - - function expectVisibleFalse(msg) { - fullData.forEach(function(trace, i) { - expect(trace.visible).toBe(false, 'visible |trace #' + i + '- ' + msg); - expect(trace._length).toBe(undefined, '_length |trace #' + i + '- ' + msg); - }); - } - - it('should set *visible:false* when locations or z or geojson is missing', function() { - _supply([ - {z: [1], geojson: 'url'}, - {locations: ['a'], geojson: 'url'}, - {locations: ['a'], z: [1]} - ]); - expectVisibleFalse(); - }); - - it('should set *visible:false* when locations or z is empty', function() { - _supply([ - {locations: [], z: [1], geojson: 'url'}, - {locations: ['a'], z: [], geojson: 'url'}, - {locations: [], z: [], geojson: 'url'} - ]); - expectVisibleFalse(); - }); - - it('should accept string (URL) and object *geojson*', function() { - _supply([ - {name: 'ok during defaults, will fail later', locations: ['a'], z: [1], geojson: 'url'}, - {name: 'ok during defaults, will fail later', locations: ['a'], z: [1], geojson: {}}, - ]); - fullData.forEach(function(trace, i) { - expect(trace.visible).toBe(true, 'visible |trace #' + i); - expect(trace._length).toBe(1, '_length |trace #' + i); - }); - - _supply([ - {name: 'no', locations: ['a'], z: [1], geojson: ''}, - {name: 'no', locations: ['a'], z: [1], geojson: []}, - {name: 'no', locations: ['a'], z: [1], geojson: true} - ]); - expectVisibleFalse(); - }); - - it('should not coerce *marker.line.color* when *marker.line.width* is *0*', function() { - _supply([{ - locations: ['CAN', 'USA'], - z: [1, 2], - geojson: 'url', - marker: { - line: { - color: 'red', - width: 0 - } - } - }]); - - expect(fullData[0].marker.line.width).toBe(0, 'mlw'); - expect(fullData[0].marker.line.color).toBe(undefined, 'mlc'); - }); -}); - -describe('Test choroplethmapbox convert:', function() { - var geojson0 = function() { - return { - type: 'FeatureCollection', - features: [ - {type: 'Feature', id: 'a', geometry: {type: 'Polygon', coordinates: []}}, - {type: 'Feature', id: 'b', geometry: {type: 'Polygon', coordinates: []}}, - {type: 'Feature', id: 'c', geometry: {type: 'Polygon', coordinates: []}} - ] - }; - }; - - var base = function() { - return { - locations: ['a', 'b', 'c'], - z: [10, 20, 5], - geojson: geojson0() - }; - }; - - function pre(trace, layout) { - var gd = {data: [Lib.extendFlat({type: 'choroplethmapbox'}, trace)]}; - if(layout) gd.layout = layout; - - supplyAllDefaults(gd); - Plots.doCalcdata(gd, gd._fullData[0]); - - return gd.calcdata[0]; - } - - function _convert(trace) { - return convertModule.convert(pre(trace)); - } - - function expectBlank(opts, msg) { - expect(opts.fill.layout.visibility).toBe('none', msg); - expect(opts.line.layout.visibility).toBe('none', msg); - expect(opts.geojson).toEqual({type: 'Point', coordinates: []}, msg); - } - - function extract(opts, k) { - return opts.geojson.features.map(function(f) { return f.properties[k]; }); - } - - it('should return early when trace is *visible:false*', function() { - var opts = _convert(Lib.extendFlat(base(), {visible: false})); - expectBlank(opts); - }); - - it('should return early when trace is has no *_length*', function() { - var opts = _convert({ - locations: [], - z: [], - geojson: geojson0 - }); - expectBlank(opts); - }); - - it('should return early if something goes wrong while fetching a GeoJSON', function() { - spyOn(loggers, 'error'); - - var opts = _convert({ - locations: ['a'], z: [1], - geojson: 'url' - }); - - expect(loggers.error).toHaveBeenCalledWith('Oops ... something went wrong when fetching url'); - expectBlank(opts); - }); - - describe('should warn when set GeoJSON is not a *FeatureCollection* or a *Feature* type and return early', function() { - beforeEach(function() { spyOn(loggers, 'warn'); }); - - it('- case missing *type* key', function() { - var opts = _convert({ - locations: ['a'], z: [1], - geojson: { - missingType: '' - } - }); - expectBlank(opts); - expect(loggers.warn).toHaveBeenCalledWith([ - 'Invalid GeoJSON type none.', - 'Traces with locationmode *geojson-id* only support *FeatureCollection* and *Feature* types.' - ].join(' ')); - }); - - it('- case invalid *type*', function() { - var opts = _convert({ - locations: ['a'], z: [1], - geojson: { - type: 'nop!' - } - }); - expectBlank(opts); - expect(loggers.warn).toHaveBeenCalledWith([ - 'Invalid GeoJSON type nop!.', - 'Traces with locationmode *geojson-id* only support *FeatureCollection* and *Feature* types.' - ].join(' ')); - }); - }); - - describe('should log when crossing a GeoJSON geometry that is not a *Polygon* or a *MultiPolygon* type', function() { - beforeEach(function() { spyOn(loggers, 'log'); }); - - it('- case missing geometry *type*', function() { - var trace = base(); - delete trace.geojson.features[1].geometry.type; - - var opts = _convert(trace); - expect(opts.geojson.features.length).toBe(2, '# of feature to be rendered'); - expect(loggers.log).toHaveBeenCalledWith([ - 'Location b does not have a valid GeoJSON geometry.', - 'Traces with locationmode *geojson-id* only support *Polygon* and *MultiPolygon* geometries.' - ].join(' ')); - }); - - it('- case invalid geometry *type*', function() { - var trace = base(); - trace.geojson.features[2].geometry.type = 'not-gonna-work'; - - var opts = _convert(trace); - expect(opts.geojson.features.length).toBe(2, '# of feature to be rendered'); - expect(loggers.log).toHaveBeenCalledWith([ - 'Location c does not have a valid GeoJSON geometry.', - 'Traces with locationmode *geojson-id* only support *Polygon* and *MultiPolygon* geometries.' - ].join(' ')); - }); - }); - - it('should log when an entry set in *locations* does not a matching feature in the GeoJSON', function() { - spyOn(loggers, 'log'); - - var trace = base(); - trace.locations = ['a', 'b', 'c', 'd']; - trace.z = [1, 2, 3, 1]; - - var opts = _convert(trace); - expect(opts.geojson.features.length).toBe(3, '# of features to be rendered'); - expect(loggers.log).toHaveBeenCalledWith('Location *d* does not have a matching feature with id-key *id*.'); - }); - - describe('should accept numbers as *locations* items', function() { - function _assert(act) { - expect(act.fill.layout.visibility).toBe('visible', 'fill layer visibility'); - expect(act.line.layout.visibility).toBe('visible', 'line layer visibility'); - expect(act.geojson.features.length).toBe(3, '# of visible features'); - expect(extract(act, 'fc')) - .toEqual(['rgb(178, 10, 28)', 'rgb(220, 220, 220)', 'rgb(240, 149, 99)']); - } - - it('- regular array case', function() { - var trace = { - locations: [1, 2, 3], - z: [20, 10, 2], - geojson: { - type: 'FeatureCollection', - features: [ - {type: 'Feature', id: '1', geometry: {type: 'Polygon', coordinates: []}}, - {type: 'Feature', id: '3', geometry: {type: 'Polygon', coordinates: []}}, - {type: 'Feature', id: '2', geometry: {type: 'Polygon', coordinates: []}} - ] - } - }; - _assert(_convert(trace)); - }); - - it('- typed array case', function() { - var trace = { - locations: new Float32Array([1, 2, 3]), - z: new Float32Array([20, 10, 2]), - geojson: { - type: 'FeatureCollection', - features: [ - {type: 'Feature', id: 1, geometry: {type: 'Polygon', coordinates: []}}, - {type: 'Feature', id: 3, geometry: {type: 'Polygon', coordinates: []}}, - {type: 'Feature', id: 2, geometry: {type: 'Polygon', coordinates: []}} - ] - } - }; - _assert(_convert(trace)); - }); - }); - - it('should handle *Feature* on 1-item *FeatureCollection* the same way', function() { - var locations = ['a']; - var z = [1]; - - var feature = { - type: 'Feature', - id: 'a', - geometry: {type: 'Polygon', coordinates: []} - }; - - var opts = _convert({ - locations: locations, - z: z, - geojson: feature - }); - - var opts2 = _convert({ - locations: locations, - z: z, - geojson: { - type: 'FeatureCollection', - features: [feature] - } - }); - - expect(opts).toEqual(opts2); - }); - - it('should fill stuff in corresponding calcdata items', function() { - var calcTrace = pre(base()); - var opts = convertModule.convert(calcTrace); - - var fullTrace = calcTrace[0].trace; - expect(fullTrace._opts).toBe(opts, 'opts ref'); - - for(var i = 0; i < calcTrace.length; i++) { - var cdi = calcTrace[i]; - expect(typeof cdi._polygons).toBe('object', '_polygons |' + i); - expect(Array.isArray(cdi.ct)).toBe(true, 'ct|' + i); - expect(typeof cdi.fIn).toBe('object', 'fIn |' + i); - expect(typeof cdi.fOut).toBe('object', 'fOut |' + i); - } - }); - - describe('should fill *fill-color* correctly', function() { - function _assert(act, exp) { - expect(act.fill.paint['fill-color']) - .toEqual({type: 'identity', property: 'fc'}); - expect(extract(act, 'fc')).toEqual(exp); - } - - it('- base case', function() { - _assert(_convert(base()), [ - 'rgb(245, 172, 122)', - 'rgb(178, 10, 28)', - 'rgb(220, 220, 220)' - ]); - }); - - it('- custom colorscale case', function() { - var trace = base(); - trace.colorscale = [[0, 'rgb(0, 255, 0)'], [1, 'rgb(0, 0, 255)']]; - trace.zmid = 10; - - _assert(_convert(trace), [ - 'rgb(0, 128, 128)', - 'rgb(0, 0, 255)', - 'rgb(0, 191, 64)' - ]); - }); - }); - - describe('should fill *fill-opacity* correctly', function() { - function _assertScalar(act, exp) { - expect(act.fill.paint['fill-opacity']).toBe(exp); - expect(act.line.paint['line-opacity']).toBe(exp); - expect(extract(act, 'mo')).toEqual([undefined, undefined, undefined]); - } - - function _assertArray(act, k, exp) { - expect(act.fill.paint['fill-opacity']).toEqual({type: 'identity', property: k}); - expect(act.line.paint['line-opacity']).toEqual({type: 'identity', property: k}); - expect(extract(act, k)).toBeCloseToArray(exp, 2); - } - - function fakeSelect(calcTrace, selectedpoints) { - if(selectedpoints === null) { - delete calcTrace[0].trace.selectedpoints; - } else { - calcTrace[0].trace.selectedpoints = selectedpoints; - } - - for(var i = 0; i < calcTrace.length; i++) { - var cdi = calcTrace[i]; - if(selectedpoints) { - if(selectedpoints.indexOf(i) !== -1) { - cdi.selected = 1; - } else { - cdi.selected = 0; - } - } else { - delete cdi.selected; - } - } - } - - it('- base case', function() { - var trace = base(); - trace.marker = {opacity: 0.4}; - _assertScalar(_convert(trace), 0.4); - }); - - it('- arrayOk case', function() { - var trace = base(); - trace.marker = {opacity: [null, 0.2, -10]}; - _assertArray(_convert(trace), 'mo', [0, 0.2, 0]); - }); - - it('- arrayOk case + bad coordinates', function() { - var trace = base(); - trace.locations = ['a', null, 'c']; - trace.marker = {opacity: [-1, 0.2, 0.9]}; - _assertArray(_convert(trace), 'mo', [0, 0.9]); - }); - - it('- selection (base)', function() { - var trace = base(); - trace.selectedpoints = [1]; - - var calcTrace = pre(trace); - _assertArray(convertModule.convert(calcTrace), 'mo2', [0.2, 1, 0.2]); - - fakeSelect(calcTrace, [1, 2]); - _assertArray(convertModule.convertOnSelect(calcTrace), 'mo2', [0.2, 1, 1]); - - fakeSelect(calcTrace, []); - _assertArray(convertModule.convertOnSelect(calcTrace), 'mo2', [0.2, 0.2, 0.2]); - - calcTrace[0].trace.unselected = {marker: {opacity: 0}}; - _assertArray(convertModule.convertOnSelect(calcTrace), 'mo2', [0, 0, 0]); - - fakeSelect(calcTrace, null); - _assertScalar(convertModule.convertOnSelect(calcTrace), 1); - }); - - it('- selection of arrayOk marker.opacity', function() { - var trace = base(); - trace.marker = {opacity: [0.4, 1, 0.8]}; - trace.selectedpoints = [1]; - - var calcTrace = pre(trace); - _assertArray(convertModule.convert(calcTrace), 'mo2', [0.08, 1, 0.16]); - - fakeSelect(calcTrace, [1, 2]); - _assertArray(convertModule.convertOnSelect(calcTrace), 'mo2', [0.08, 1, 0.8]); - - calcTrace[0].trace.selected = {marker: {opacity: 1}}; - _assertArray(convertModule.convertOnSelect(calcTrace), 'mo2', [0.08, 1, 1]); - - fakeSelect(calcTrace, []); - _assertArray(convertModule.convertOnSelect(calcTrace), 'mo2', [0.08, 0.2, 0.16]); - - calcTrace[0].trace.unselected = {marker: {opacity: 0}}; - _assertArray(convertModule.convertOnSelect(calcTrace), 'mo2', [0, 0, 0]); - - fakeSelect(calcTrace, null); - _assertArray(convertModule.convertOnSelect(calcTrace), 'mo', [0.4, 1, 0.8]); - }); - }); - - describe('should fill *line-color*, *line-width* correctly', function() { - it('- base case', function() { - var trace = base(); - trace.marker = {line: {color: 'blue', width: 3}}; - - var opts = _convert(trace); - expect(opts.line.paint['line-color']).toBe('blue'); - expect(opts.line.paint['line-width']).toBe(3); - expect(extract(opts, 'mlc')).toEqual([undefined, undefined, undefined]); - expect(extract(opts, 'mlw')).toEqual([undefined, undefined, undefined]); - }); - - it('- arrayOk case', function() { - var trace = base(); - trace.marker = { - line: { - color: ['blue', 'red', 'black'], - width: [0.1, 2, 10] - } - }; - - var opts = _convert(trace); - expect(opts.line.paint['line-color']).toEqual({type: 'identity', property: 'mlc'}); - expect(opts.line.paint['line-width']).toEqual({type: 'identity', property: 'mlw'}); - expect(extract(opts, 'mlc')).toEqual(['blue', 'red', 'black']); - expect(extract(opts, 'mlw')).toEqual([0.1, 2, 10]); - }); - }); - - it('should find correct centroid (single polygon case)', function() { - var trace = base(); - - var coordsIn = [ - [ - [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], - [100.0, 1.0], [100.0, 0.0] - ] - ]; - - trace.geojson.features[0].geometry.coordinates = coordsIn; - var calcTrace = pre(trace); - var opts = convertModule.convert(calcTrace); - - expect(opts.geojson.features[0].geometry.coordinates).toBe(coordsIn); - expect(calcTrace[0].ct).toEqual([100.5, 0.5]); - }); - - it('should find correct centroid (multi-polygon case)', function() { - var trace = base(); - - var coordsIn = [ - [ - // this one has the bigger area - [[30, 20], [47, 40], [10, 33], [30, 20]] - ], - [ - [[15, 5], [40, 10], [10, 20], [5, 10], [15, 5]] - ] - ]; - - trace.geojson.features[0].geometry.type = 'MultiPolygon'; - trace.geojson.features[0].geometry.coordinates = coordsIn; - var calcTrace = pre(trace); - var opts = convertModule.convert(calcTrace); - - expect(opts.geojson.features[0].geometry.coordinates).toBe(coordsIn); - expect(calcTrace[0].ct).toEqual([29, 31]); - }); -}); - -describe('Test choroplethmapbox hover:', function() { - var gd; - - afterEach(function(done) { - Plotly.purge(gd); - destroyGraphDiv(); - setTimeout(done, 200); - }); - - function transformPlot(gd, transformString) { - gd.style.webkitTransform = transformString; - gd.style.MozTransform = transformString; - gd.style.msTransform = transformString; - gd.style.OTransform = transformString; - gd.style.transform = transformString; - } - - function run(hasCssTransform, s, done) { - gd = createGraphDiv(); - var scale = 1; - if(hasCssTransform) { - scale = 0.5; - } - - var fig = Lib.extendDeep({}, - s.mock || require('../../image/mocks/mapbox_choropleth0.json') - ); - - if(s.patch) { - fig = s.patch(fig); - } - - if(!fig.layout) fig.layout = {}; - if(!fig.layout.mapbox) fig.layout.mapbox = {}; - fig.layout.mapbox.accesstoken = MAPBOX_ACCESS_TOKEN; - - var pos = s.pos || [270, 220]; - - return Plotly.newPlot(gd, fig).then(function() { - if(hasCssTransform) transformPlot(gd, 'translate(-25%, -25%) scale(0.5)'); - - var to = setTimeout(function() { - failTest('no event data received'); - done(); - }, 100); - - gd.on('plotly_hover', function(d) { - clearTimeout(to); - assertHoverLabelContent(s); - - var msg = ' - event data ' + s.desc; - var actual = d.points || []; - var exp = s.evtPts; - expect(actual.length).toBe(exp.length, 'pt length' + msg); - for(var i = 0; i < exp.length; i++) { - for(var k in exp[i]) { - var m = 'key ' + k + ' in pt ' + i + msg; - var matcher = k === 'properties' ? 'toEqual' : 'toBe'; - expect(actual[i][k])[matcher](exp[i][k], m); - } - } - - // w/o this purge gets called before - // hover throttle is complete - setTimeout(done, 0); - }); - - mouseEvent('mousemove', scale * pos[0], scale * pos[1]); - }) - .catch(failTest); - } - - var specs = [{ - desc: 'basic', - nums: '10', - name: 'NY', - evtPts: [{location: 'NY', z: 10, pointNumber: 0, curveNumber: 0, properties: {name: 'New York'}}] - }, { - desc: 'with a hovertemplate using values in *properties*', - patch: function(fig) { - fig.data.forEach(function(t) { - t.hovertemplate = '%{z:.3f}PROP::%{properties.name}'; - }); - return fig; - }, - nums: '10.000', - name: 'PROP::New York', - evtPts: [{location: 'NY', z: 10, pointNumber: 0, curveNumber: 0, properties: {name: 'New York'}}] - }, { - desc: 'with "typeof number" locations[i] and feature id (in *name* label case)', - patch: function() { - var fig = Lib.extendDeep({}, require('../../image/mocks/mapbox_choropleth-raw-geojson.json')); - fig.data = [fig.data[1]]; - fig.data[0].locations = [100]; - fig.data[0].geojson.id = 100; - return fig; - }, - nums: '10', - name: '100', - evtPts: [{location: 100, z: 10, pointNumber: 0, curveNumber: 0}] - }, { - desc: 'with "typeof number" locations[i] and feature id (in *nums* label case)', - patch: function() { - var fig = Lib.extendDeep({}, require('../../image/mocks/mapbox_choropleth-raw-geojson.json')); - fig.data = [fig.data[1]]; - fig.data[0].locations = [100]; - fig.data[0].geojson.id = 100; - fig.data[0].hoverinfo = 'location+name'; - return fig; - }, - nums: '100', - name: 'trace 0', - evtPts: [{location: 100, z: 10, pointNumber: 0, curveNumber: 0}] - }, { - desc: 'with "typeof number" locations[i] and feature id (hovertemplate case)', - patch: function() { - var fig = Lib.extendDeep({}, require('../../image/mocks/mapbox_choropleth-raw-geojson.json')); - fig.data = [fig.data[1]]; - fig.data[0].locations = [100]; - fig.data[0].geojson.id = 100; - fig.data[0].hovertemplate = '### %{location}%{ct[0]:.1f} | %{ct[1]:.1f} ###'; - return fig; - }, - nums: '### 100', - name: '-86.7 | 31.9 ###', - evtPts: [{location: 100, z: 10, pointNumber: 0, curveNumber: 0}] - }]; - - specs.forEach(function(s) { - [false, true].forEach(function(hasCssTransform) { - it('@gl should generate correct hover labels ' + s.desc + ', hasCssTransform: ' + hasCssTransform, function(done) { - run(hasCssTransform, s, done); - }); - }); - }); -}); - -describe('Test choroplethmapbox interactions:', function() { - var gd; - - var geojson = { - type: 'Feature', - id: 'AL', - geometry: { - type: 'Polygon', - coordinates: [[ - [-87.359296, 35.00118 ], [-85.606675, 34.984749 ], [-85.431413, 34.124869 ], [-85.184951, 32.859696 ], - [-85.069935, 32.580372 ], [-84.960397, 32.421541 ], [-85.004212, 32.322956 ], [-84.889196, 32.262709 ], - [-85.058981, 32.13674 ], [-85.053504, 32.01077 ], [-85.141136, 31.840985 ], [-85.042551, 31.539753 ], - [-85.113751, 31.27686 ], [-85.004212, 31.003013 ], [-85.497137, 30.997536 ], [-87.600282, 30.997536 ], - [-87.633143, 30.86609 ], [-87.408589, 30.674397 ], [-87.446927, 30.510088 ], [-87.37025, 30.427934 ], - [-87.518128, 30.280057 ], [-87.655051, 30.247195 ], [-87.90699, 30.411504 ], [-87.934375, 30.657966 ], - [-88.011052, 30.685351 ], [-88.10416, 30.499135 ], [-88.137022, 30.318396 ], [-88.394438, 30.367688 ], - [-88.471115, 31.895754 ], [-88.241084, 33.796253 ], [-88.098683, 34.891641 ], [-88.202745, 34.995703 ], - [-87.359296, 35.00118 ] - ]] - } - }; - - beforeEach(function() { - gd = createGraphDiv(); - }); - - afterEach(function(done) { - Plotly.purge(gd); - destroyGraphDiv(); - setTimeout(done, 200); - }); - - it('@gl should be able to add and remove traces', function(done) { - function _assert(msg, exp) { - var map = gd._fullLayout.mapbox._subplot.map; - var layers = map.getStyle().layers; - - expect(layers.length).toBe(exp.layerCnt, 'total # of layers |' + msg); - } - - var trace0 = { - type: 'choroplethmapbox', - locations: ['AL'], - z: [10], - geojson: geojson - }; - - var trace1 = { - type: 'choroplethmapbox', - locations: ['AL'], - z: [1], - geojson: geojson, - marker: {opacity: 0.3} - }; - - Plotly.newPlot(gd, - [trace0, trace1], - {mapbox: {style: 'basic'}}, - {mapboxAccessToken: MAPBOX_ACCESS_TOKEN} - ) - .then(function() { - _assert('base', { layerCnt: 24 }); - }) - .then(function() { return Plotly.deleteTraces(gd, [0]); }) - .then(function() { - _assert('w/o trace0', { layerCnt: 22 }); - }) - .then(function() { return Plotly.addTraces(gd, [trace0]); }) - .then(function() { - _assert('after adding trace0', { layerCnt: 24 }); - }) - .then(done, done.fail); - }); - - it('@gl should be able to restyle *below*', function(done) { - function getLayerIds() { - var subplot = gd._fullLayout.mapbox._subplot; - var layers = subplot.map.getStyle().layers; - var layerIds = layers.map(function(l) { return l.id; }); - return layerIds; - } - - Plotly.newPlot(gd, [{ - type: 'choroplethmapbox', - locations: ['AL'], - z: [10], - geojson: geojson, - uid: 'a' - }], {}, {mapboxAccessToken: MAPBOX_ACCESS_TOKEN}) - .then(function() { - expect(getLayerIds()).withContext('default *below*').toEqual([ - 'background', 'landuse_overlay_national_park', 'landuse_park', - 'waterway', 'water', - 'plotly-trace-layer-a-fill', 'plotly-trace-layer-a-line', - 'building', 'tunnel_minor', 'tunnel_major', 'road_minor', 'road_major', - 'bridge_minor case', 'bridge_major case', 'bridge_minor', 'bridge_major', - 'admin_country', 'poi_label', 'road_major_label', - 'place_label_other', 'place_label_city', 'country_label' - ]); - }) - .then(function() { return Plotly.restyle(gd, 'below', ''); }) - .then(function() { - expect(getLayerIds()).withContext('*below* set to \'\'').toEqual([ - 'background', 'landuse_overlay_national_park', 'landuse_park', - 'waterway', 'water', - 'building', 'tunnel_minor', 'tunnel_major', 'road_minor', 'road_major', - 'bridge_minor case', 'bridge_major case', 'bridge_minor', 'bridge_major', - 'admin_country', 'poi_label', 'road_major_label', - 'place_label_other', 'place_label_city', 'country_label', - 'plotly-trace-layer-a-fill', 'plotly-trace-layer-a-line' - ]); - }) - .then(function() { return Plotly.restyle(gd, 'below', 'place_label_other'); }) - .then(function() { - expect(getLayerIds()).withContext('*below* set to same base layer').toEqual([ - 'background', 'landuse_overlay_national_park', 'landuse_park', - 'waterway', 'water', - 'building', 'tunnel_minor', 'tunnel_major', 'road_minor', 'road_major', - 'bridge_minor case', 'bridge_major case', 'bridge_minor', 'bridge_major', - 'admin_country', 'poi_label', 'road_major_label', - 'plotly-trace-layer-a-fill', 'plotly-trace-layer-a-line', - 'place_label_other', 'place_label_city', 'country_label', - ]); - }) - .then(function() { return Plotly.restyle(gd, 'below', null); }) - .then(function() { - expect(getLayerIds()).withContext('back to default *below*').toEqual([ - 'background', 'landuse_overlay_national_park', 'landuse_park', - 'waterway', 'water', - 'plotly-trace-layer-a-fill', 'plotly-trace-layer-a-line', - 'building', 'tunnel_minor', 'tunnel_major', 'road_minor', 'road_major', - 'bridge_minor case', 'bridge_major case', 'bridge_minor', 'bridge_major', - 'admin_country', 'poi_label', 'road_major_label', - 'place_label_other', 'place_label_city', 'country_label' - ]); - }) - .then(done, done.fail); - }, 5 * jasmine.DEFAULT_TIMEOUT_INTERVAL); -}); diff --git a/test/jasmine/tests/densitymapbox_test.js b/test/jasmine/tests/densitymapbox_test.js deleted file mode 100644 index c8cadd7cefb..00000000000 --- a/test/jasmine/tests/densitymapbox_test.js +++ /dev/null @@ -1,523 +0,0 @@ -var Plotly = require('../../../lib/index'); -var Plots = require('../../../src/plots/plots'); -var Lib = require('../../../src/lib'); - -var convert = require('../../../src/traces/densitymapbox/convert'); -var MAPBOX_ACCESS_TOKEN = require('../../../build/credentials.json').MAPBOX_ACCESS_TOKEN; - -var createGraphDiv = require('../assets/create_graph_div'); -var destroyGraphDiv = require('../assets/destroy_graph_div'); -var failTest = require('../assets/fail_test'); -var mouseEvent = require('../assets/mouse_event'); -var supplyAllDefaults = require('../assets/supply_defaults'); - -var assertHoverLabelContent = require('../assets/custom_assertions').assertHoverLabelContent; - -describe('Test densitymapbox defaults:', function() { - var gd; - var fullData; - - function _supply(opts, layout) { - gd = {}; - opts = Array.isArray(opts) ? opts : [opts]; - - gd.data = opts.map(function(o) { - return Lib.extendFlat({type: 'densitymapbox'}, o || {}); - }); - gd.layout = layout || {}; - - supplyAllDefaults(gd); - fullData = gd._fullData; - } - - it('should set *visible:false* when *lon* and/or *lat* is missing or empty', function() { - _supply([ - {}, - {lon: [1]}, - {lat: [1]}, - {lon: [], lat: []}, - {lon: [1], lat: []}, - {lon: [], lat: [1]} - ]); - - fullData.forEach(function(trace, i) { - expect(trace.visible).toBe(false, 'visible |trace #' + i); - expect(trace._length).toBe(undefined, '_length |trace #' + i); - }); - }); -}); - -describe('Test densitymapbox convert:', function() { - var base = function() { - return { - lon: [10, 20, 30], - lat: [15, 25, 35], - z: [1, 20, 5], - }; - }; - - function pre(trace, layout) { - var gd = {data: [Lib.extendFlat({type: 'densitymapbox'}, trace)]}; - if(layout) gd.layout = layout; - - supplyAllDefaults(gd); - Plots.doCalcdata(gd, gd._fullData[0]); - - return gd.calcdata[0]; - } - - function _convert(trace) { - return convert(pre(trace)); - } - - function expectBlank(opts, msg) { - expect(opts.heatmap.layout.visibility).toBe('none', msg); - expect(opts.geojson).toEqual({type: 'Point', coordinates: []}, msg); - } - - function extract(opts, k) { - return opts.geojson.features.map(function(f) { return f.properties[k]; }); - } - - it('should return early when trace is *visible:false*', function() { - var opts = _convert(Lib.extendFlat(base(), {visible: false})); - expectBlank(opts); - }); - - it('should return early when trace is has no *_length*', function() { - var opts = _convert({ - lon: [], - lat: [], - z: [], - }); - expectBlank(opts); - }); - - describe('should fill in *z* in GeoJSON properties', function() { - function _assert(act, prop, paint) { - expect(extract(act, 'z')).toEqual(prop); - expect(act.heatmap.paint['heatmap-weight']).toEqual(paint); - } - - it('- base', function() { - var opts = _convert(base()); - _assert(opts, [1, 20, 5], [ - 'interpolate', ['linear'], - ['get', 'z'], - 1, 0, - 20, 1 - ]); - }); - - it('- with BADNUMs', function() { - var opts = _convert({ - lon: [10, null, 30, 40], - lat: [null, 25, 35, 45], - z: [1, 20, null, 10] - }); - _assert(opts, [0, 10], [ - 'interpolate', ['linear'], - ['get', 'z'], - 1, 0, - 20, 1 - ]); - }); - - it('- w/ set zmin/zmax', function() { - var opts = _convert(Lib.extendFlat({}, base(), {zmin: 0, zmax: 100})); - _assert(opts, [1, 20, 5], [ - 'interpolate', ['linear'], - ['get', 'z'], - 0, 0, - 100, 1 - ]); - }); - - it('- w/o z', function() { - var opts = _convert({ - lon: [10, 20, 30, 40], - lat: [15, 25, 35, 45], - }); - _assert(opts, [undefined, undefined, undefined, undefined], 1); - }); - }); - - describe('should fill in *radius* settings', function() { - function _assert(act, prop, paint) { - expect(extract(act, 'r')).toEqual(prop); - expect(act.heatmap.paint['heatmap-radius']).toEqual(paint); - } - - it('- base', function() { - var opts = _convert(base()); - _assert(opts, [undefined, undefined, undefined], 30); - }); - - it('- arrayOk', function() { - var opts = _convert({ - lon: [10, 20, 30, 40], - lat: [15, 25, 35, 45], - z: [1, 20, 5, 10], - radius: [20, '2', -100, 'not-gonna-work'] - }); - _assert(opts, [20, 2, 0, 0], {type: 'identity', property: 'r'}); - }); - }); - - it('should propagate the trace opacity', function() { - var opts = _convert(Lib.extendFlat({}, base(), {opacity: 0.2})); - expect(opts.heatmap.paint['heatmap-opacity']).toBe(0.2); - }); - - describe('should propagate colorscale settings', function() { - function _assert(act, exp) { - expect(act.heatmap.paint['heatmap-color']).toEqual(exp); - } - - it('- base', function() { - var opts = _convert(Lib.extendFlat(base(), { - colorscale: [ - [0, 'rgb(0, 0, 0)'], - [1, 'rgb(255, 255, 255)'] - ] - })); - _assert(opts, [ - 'interpolate', ['linear'], - ['heatmap-density'], - 0, 'rgba(0, 0, 0, 0)', - 1, 'rgb(255, 255, 255)' - ]); - }); - - it('- with rgba in colorscale[0][1]', function() { - var opts = _convert(Lib.extendFlat(base(), { - colorscale: [ - [0, 'rgba(0, 0, 0, 0.2)'], - [1, 'rgb(255, 255, 255)'] - ] - })); - _assert(opts, [ - 'interpolate', ['linear'], - ['heatmap-density'], - 0, 'rgba(0, 0, 0, 0.2)', - 1, 'rgb(255, 255, 255)' - ]); - }); - - it('- w/ reversescale:true', function() { - var opts = _convert(Lib.extendFlat(base(), { - colorscale: [ - [0, 'rgb(0, 0, 0)'], - [1, 'rgb(255, 255, 255)'] - ], - reversescale: true - })); - _assert(opts, [ - 'interpolate', ['linear'], - ['heatmap-density'], - 0, 'rgba(255, 255, 255, 0)', - 1, 'rgb(0, 0, 0)' - ]); - }); - - it('- with rgba in colorscale[0][1] and reversescale:true', function() { - var opts = _convert(Lib.extendFlat(base(), { - colorscale: [ - [0, 'rgba(0, 0, 0, 0.2)'], - [1, 'rgba(255, 255, 255, 0.2)'] - ], - reversescale: true - })); - _assert(opts, [ - 'interpolate', ['linear'], - ['heatmap-density'], - 0, 'rgba(255, 255, 255, 0.2)', - 1, 'rgba(0, 0, 0, 0.2)' - ]); - }); - }); - - it('should work with typed array', function() { - var opts = _convert({ - lon: new Float32Array([10, 20, 30]), - lat: new Float32Array([15, 25, 35]), - z: new Float32Array([1, 20, 5]), - radius: new Float32Array([30, 20, 25]) - }); - - var coords = opts.geojson.features.map(function(f) { return f.geometry.coordinates; }); - expect(coords).toEqual([ [10, 15], [20, 25], [30, 35] ]); - - expect(extract(opts, 'z')).toEqual([1, 20, 5]); - expect(extract(opts, 'r')).toEqual([30, 20, 25]); - - var paint = opts.heatmap.paint; - expect(paint['heatmap-weight']).toEqual([ - 'interpolate', ['linear'], - ['get', 'z'], - 1, 0, - 20, 1 - ]); - expect(paint['heatmap-radius']).toEqual({type: 'identity', property: 'r'}); - expect(paint['heatmap-color']).toEqual([ - 'interpolate', ['linear'], - ['heatmap-density'], - 0, 'rgba(220, 220, 220, 0)', - 0.2, 'rgb(245,195,157)', - 0.4, 'rgb(245,160,105)', - 1, 'rgb(178,10,28)' - ]); - }); -}); - -describe('Test densitymapbox hover:', function() { - var gd; - - afterEach(function(done) { - Plotly.purge(gd); - destroyGraphDiv(); - setTimeout(done, 200); - }); - - function run(s, done) { - gd = createGraphDiv(); - - var fig = Lib.extendDeep({}, - s.mock || require('../../image/mocks/mapbox_density0.json') - ); - - if(s.patch) { - fig = s.patch(fig); - } - - if(!fig.layout) fig.layout = {}; - if(!fig.layout.mapbox) fig.layout.mapbox = {}; - fig.layout.mapbox.accesstoken = MAPBOX_ACCESS_TOKEN; - - var pos = s.pos || [353, 143]; - - return Plotly.newPlot(gd, fig).then(function() { - var to = setTimeout(function() { - failTest('no event data received'); - done(); - }, 100); - - gd.on('plotly_hover', function(d) { - clearTimeout(to); - assertHoverLabelContent(s); - - var msg = ' - event data ' + s.desc; - var actual = d.points || []; - var exp = s.evtPts; - expect(actual.length).toBe(exp.length, 'pt length' + msg); - for(var i = 0; i < exp.length; i++) { - for(var k in exp[i]) { - var m = 'key ' + k + ' in pt ' + i + msg; - var matcher = k === 'properties' ? 'toEqual' : 'toBe'; - expect(actual[i][k])[matcher](exp[i][k], m); - } - } - - // w/o this purge gets called before - // hover throttle is complete - setTimeout(done, 0); - }); - - mouseEvent('mousemove', pos[0], pos[1]); - }) - .catch(failTest); - } - - var specs = [{ - desc: 'basic', - nums: '3\n(25°, 20°)', - name: '', - evtPts: [{lon: 20, lat: 25, z: 3, pointNumber: 1, curveNumber: 0}] - }, { - desc: 'with a hovertemplate', - patch: function(fig) { - fig.data.forEach(function(t) { - t.hovertemplate = '%{z:.3f}%{lon} || %{lat}'; - }); - return fig; - }, - nums: '3.000', - name: '20 || 25', - evtPts: [{lon: 20, lat: 25, z: 3, pointNumber: 1, curveNumber: 0}] - }, { - desc: 'w/o z flag', - patch: function(fig) { - fig.data.forEach(function(t) { - t.hoverinfo = 'lon+lat+name'; - }); - return fig; - }, - nums: '(25°, 20°)', - name: 'trace 0', - evtPts: [{lon: 20, lat: 25, z: 3, pointNumber: 1, curveNumber: 0}] - }, { - desc: 'w/o z data', - patch: function(fig) { - fig.data.forEach(function(t) { - delete t.z; - }); - return fig; - }, - nums: '(25°, 20°)', - name: '', - evtPts: [{lon: 20, lat: 25, pointNumber: 1, curveNumber: 0}] - }]; - - specs.forEach(function(s) { - it('@gl should generate correct hover labels ' + s.desc, function(done) { - run(s, done); - }); - }); -}); - -describe('Test densitymapbox interactions:', function() { - var gd; - - beforeEach(function() { - gd = createGraphDiv(); - }); - - afterEach(function(done) { - Plotly.purge(gd); - destroyGraphDiv(); - setTimeout(done, 200); - }); - - it('@gl should be able to add and remove traces', function(done) { - function _assert(msg, exp) { - var map = gd._fullLayout.mapbox._subplot.map; - var layers = map.getStyle().layers; - - expect(layers.length).toBe(exp.layerCnt, 'total # of layers |' + msg); - } - - var trace0 = { - type: 'densitymapbox', - lon: [10, 20, 30], - lat: [15, 25, 35], - z: [1, 20, 5], - }; - - var trace1 = { - type: 'densitymapbox', - lon: [-10, -20, -30], - lat: [15, 25, 35], - z: [1, 20, 5], - }; - - Plotly.newPlot(gd, - [trace0, trace1], - {mapbox: {style: 'basic'}}, - {mapboxAccessToken: MAPBOX_ACCESS_TOKEN} - ) - .then(function() { - _assert('base', { layerCnt: 22 }); - }) - .then(function() { return Plotly.deleteTraces(gd, [0]); }) - .then(function() { - _assert('w/o trace0', { layerCnt: 21 }); - }) - .then(function() { return Plotly.addTraces(gd, [trace0]); }) - .then(function() { - _assert('after adding trace0', { layerCnt: 22 }); - }) - .then(done, done.fail); - }); - - it('@gl should be able to restyle *below*', function(done) { - function getLayerIds() { - var subplot = gd._fullLayout.mapbox._subplot; - var layers = subplot.map.getStyle().layers; - var layerIds = layers.map(function(l) { return l.id; }); - return layerIds; - } - - Plotly.newPlot(gd, [{ - type: 'densitymapbox', - lon: [10, 20, 30], - lat: [15, 25, 35], - z: [1, 20, 5], - uid: 'a' - }], {}, {mapboxAccessToken: MAPBOX_ACCESS_TOKEN}) - .then(function() { - expect(getLayerIds()).withContext('default *below*').toEqual([ - 'background', 'landuse_overlay_national_park', 'landuse_park', - 'waterway', 'water', - 'building', 'tunnel_minor', 'tunnel_major', 'road_minor', 'road_major', - 'bridge_minor case', 'bridge_major case', 'bridge_minor', 'bridge_major', - 'admin_country', - 'plotly-trace-layer-a-heatmap', - 'poi_label', 'road_major_label', - 'place_label_other', 'place_label_city', 'country_label' - ]); - }) - .then(function() { return Plotly.restyle(gd, 'below', ''); }) - .then(function() { - expect(getLayerIds()).withContext('default *below*').toEqual([ - 'background', 'landuse_overlay_national_park', 'landuse_park', - 'waterway', 'water', - 'building', 'tunnel_minor', 'tunnel_major', 'road_minor', 'road_major', - 'bridge_minor case', 'bridge_major case', 'bridge_minor', 'bridge_major', - 'admin_country', 'poi_label', 'road_major_label', - 'place_label_other', 'place_label_city', 'country_label', - 'plotly-trace-layer-a-heatmap' - ]); - }) - .then(function() { return Plotly.restyle(gd, 'below', 'place_label_other'); }) - .then(function() { - expect(getLayerIds()).withContext('default *below*').toEqual([ - 'background', 'landuse_overlay_national_park', 'landuse_park', - 'waterway', 'water', - 'building', 'tunnel_minor', 'tunnel_major', 'road_minor', 'road_major', - 'bridge_minor case', 'bridge_major case', 'bridge_minor', 'bridge_major', - 'admin_country', 'poi_label', 'road_major_label', - 'plotly-trace-layer-a-heatmap', - 'place_label_other', 'place_label_city', 'country_label', - ]); - }) - .then(function() { return Plotly.restyle(gd, 'below', null); }) - .then(function() { - expect(getLayerIds()).withContext('back to default *below*').toEqual([ - 'background', 'landuse_overlay_national_park', 'landuse_park', - 'waterway', 'water', - 'building', 'tunnel_minor', 'tunnel_major', 'road_minor', 'road_major', - 'bridge_minor case', 'bridge_major case', 'bridge_minor', 'bridge_major', - 'admin_country', - 'plotly-trace-layer-a-heatmap', - 'poi_label', 'road_major_label', - 'place_label_other', 'place_label_city', 'country_label' - ]); - }) - .then(done, done.fail); - }, 5 * jasmine.DEFAULT_TIMEOUT_INTERVAL); - - it('@gl should be able to restyle from and to *scattermapbox*', function(done) { - function _assert(msg, exp) { - var traceHash = gd._fullLayout.mapbox._subplot.traceHash; - expect(Object.keys(traceHash).length).toBe(1, 'one visible trace| ' + msg); - for(var k in traceHash) { - expect(traceHash[k].type).toBe(exp, 'trace type| ' + msg); - } - } - - Plotly.newPlot(gd, [{ - type: 'densitymapbox', - lon: [10, 20, 30], - lat: [15, 25, 35], - z: [1, 20, 5] - }], {}, { - mapboxAccessToken: MAPBOX_ACCESS_TOKEN - }) - .then(function() { _assert('after first', 'densitymapbox'); }) - .then(function() { return Plotly.restyle(gd, 'type', 'scattermapbox'); }) - .then(function() { _assert('after restyle to scattermapbox', 'scattermapbox'); }) - .then(function() { return Plotly.restyle(gd, 'type', 'densitymapbox'); }) - .then(function() { _assert('back to densitymapbox', 'densitymapbox'); }) - .then(done, done.fail); - }, 5 * jasmine.DEFAULT_TIMEOUT_INTERVAL); -}); diff --git a/test/jasmine/tests/draw_newshape_test.js b/test/jasmine/tests/draw_newshape_test.js index f0ea4d2b809..657df5b2c0f 100644 --- a/test/jasmine/tests/draw_newshape_test.js +++ b/test/jasmine/tests/draw_newshape_test.js @@ -679,70 +679,6 @@ describe('Draw new shapes to layout', function() { } ] }, - { - name: 'mapbox', - json: require('../../image/mocks/mapbox_angles'), - testPos: [ - function(pos) { - return assertPos(pos, - 'M0.2076923076923077,0.8725490196078431L0.2846153846153846,0.9705882352941176L0.33076923076923076,0.9705882352941176L0.2076923076923077,0.8725490196078431' - ); - }, - function(pos) { - return assertPos(pos, - 'M0.09230769230769231,0.9215686274509804L0.24615384615384617,0.9215686274509804L0.24615384615384617,0.7254901960784313L0.09230769230769231,0.7254901960784313Z' - ); - }, - function(pos) { - return assertPos(pos, { - x0: 0.2076923076923077, - y0: 0.7745098039215687, - x1: 0.36153846153846153, - y1: 0.5784313725490196 - }); - }, - function(pos) { - return assertPos(pos, { - x0: 0.13076923076923078, - y0: 0.8725490196078431, - x1: 0.05384615384615385, - y1: 0.9705882352941176 - }); - }, - function(pos) { - return assertPos(pos, { - x0: 0.021983572125146553, - y0: 0.6358614154536182, - x1: 0.23955488941331504, - y1: 0.9131581923895189 - }); - }, - function(pos) { - return assertPos(pos, { - x0: 0.2076923076923077, - y0: 0.6764705882352943, - x1: 0.053846153846153794, - y1: 0.872549019607843 - }); - }, - function(pos) { - return assertPos(pos, { - x0: 0.053846153846153835, - y0: 0.872549019607843, - x1: 0.2076923076923078, - y1: 0.6764705882352943 - }); - }, - function(pos) { - return assertPos(pos, { - x0: 0.1851620600912729, - y0: 0.3862943162113073, - x1: 0.07637640144718866, - y1: 1.1627252916318298 - }); - } - ] - }, { name: 'map', json: require('../../image/mocks/map_angles'), diff --git a/test/jasmine/tests/mapbox_test.js b/test/jasmine/tests/mapbox_test.js deleted file mode 100644 index 0432710fade..00000000000 --- a/test/jasmine/tests/mapbox_test.js +++ /dev/null @@ -1,2242 +0,0 @@ -var Plotly = require('../../../lib/index'); -var Lib = require('../../../src/lib'); -var Fx = require('../../../src/components/fx'); - -var constants = require('../../../src/plots/mapbox/constants'); -var supplyLayoutDefaults = require('../../../src/plots/mapbox/layout_defaults'); - -var d3Select = require('../../strict-d3').select; -var d3SelectAll = require('../../strict-d3').selectAll; -var createGraphDiv = require('../assets/create_graph_div'); -var destroyGraphDiv = require('../assets/destroy_graph_div'); -var mouseEvent = require('../assets/mouse_event'); -var click = require('../assets/click'); -var delay = require('../assets/delay'); - -var supplyAllDefaults = require('../assets/supply_defaults'); - -var customAssertions = require('../assets/custom_assertions'); -var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle; -var assertHoverLabelContent = customAssertions.assertHoverLabelContent; - -var SORTED_EVENT_KEYS = [ - 'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex', - 'lon', 'lat', - 'bbox' -].sort(); - -var MAPBOX_ACCESS_TOKEN = require('../../../build/credentials.json').MAPBOX_ACCESS_TOKEN; -var TRANSITION_DELAY = 500; -var MOUSE_DELAY = 100; -var LONG_TIMEOUT_INTERVAL = 5 * jasmine.DEFAULT_TIMEOUT_INTERVAL; - -var noop = function() {}; - -Plotly.setPlotConfig({ - mapboxAccessToken: MAPBOX_ACCESS_TOKEN -}); - -describe('mapbox defaults', function() { - var layoutIn, layoutOut, fullData; - - beforeEach(function() { - layoutOut = { font: { color: 'red' }, _subplots: {mapbox: ['mapbox']} }; - - // needs a mapbox-ref in a trace in order to be detected - fullData = [{ type: 'scattermapbox', subplot: 'mapbox' }]; - }); - - it('should fill empty containers', function() { - layoutIn = {}; - - supplyLayoutDefaults(layoutIn, layoutOut, fullData); - expect(layoutIn).toEqual({ mapbox: {} }); - }); - - it('should copy ref to input container in full (for updating on map move)', function() { - var mapbox = { style: 'light '}; - - layoutIn = { mapbox: mapbox }; - - supplyLayoutDefaults(layoutIn, layoutOut, fullData); - expect(layoutOut.mapbox._input).toBe(mapbox); - }); - - it('should accept both string and object style', function() { - var mapboxStyleJSON = { - id: 'cdsa213wqdsa', - owner: 'johnny' - }; - - layoutIn = { - mapbox: { style: 'light' }, - mapbox2: { style: mapboxStyleJSON } - }; - - fullData.push({ type: 'scattermapbox', subplot: 'mapbox2' }); - layoutOut._subplots.mapbox.push('mapbox2'); - - supplyLayoutDefaults(layoutIn, layoutOut, fullData); - expect(layoutOut.mapbox.style).toEqual('light'); - expect(layoutOut.mapbox2.style).toBe(mapboxStyleJSON); - }); - - it('should fill layer containers', function() { - layoutIn = { - mapbox: { - layers: [{}, {}] - } - }; - - supplyLayoutDefaults(layoutIn, layoutOut, fullData); - expect(layoutOut.mapbox.layers[0].sourcetype).toEqual('geojson'); - expect(layoutOut.mapbox.layers[1].sourcetype).toEqual('geojson'); - }); - - it('should skip over non-object layer containers', function() { - layoutIn = { - mapbox: { - layers: [{}, null, 'remove', {}] - } - }; - - supplyLayoutDefaults(layoutIn, layoutOut, fullData); - expect(layoutOut.mapbox.layers).toEqual([jasmine.objectContaining({ - sourcetype: 'geojson', - _index: 0 - }), jasmine.objectContaining({ - visible: false - }), jasmine.objectContaining({ - visible: false - }), jasmine.objectContaining({ - sourcetype: 'geojson', - _index: 3 - })]); - }); - - it('should coerce \'sourcelayer\' only for *vector* \'sourcetype\'', function() { - layoutIn = { - mapbox: { - layers: [{ - sourcetype: 'vector', - sourcelayer: 'layer0' - }, { - sourcetype: 'geojson', - sourcelayer: 'layer0' - }] - } - }; - - supplyLayoutDefaults(layoutIn, layoutOut, fullData); - expect(layoutOut.mapbox.layers[0].sourcelayer).toEqual('layer0'); - expect(layoutOut.mapbox.layers[1].sourcelayer).toBeUndefined(); - }); - - it('should only coerce relevant layer style attributes', function() { - var base = { - line: { width: 3 }, - fill: { outlinecolor: '#d3d3d3' }, - circle: { radius: 20 }, - symbol: { icon: 'monument' } - }; - - layoutIn = { - mapbox: { - layers: [ - Lib.extendFlat({}, base, { - type: 'line', - color: 'red' - }), - Lib.extendFlat({}, base, { - type: 'fill', - color: 'blue' - }), - Lib.extendFlat({}, base, { - type: 'circle', - color: 'green' - }), - Lib.extendFlat({}, base, { - type: 'symbol', - color: 'yellow' - }) - ] - } - }; - - supplyLayoutDefaults(layoutIn, layoutOut, fullData); - - expect(layoutOut.mapbox.layers[0].color).toEqual('red'); - expect(layoutOut.mapbox.layers[0].line.width).toEqual(3); - expect(layoutOut.mapbox.layers[0].fill).toBeUndefined(); - expect(layoutOut.mapbox.layers[0].circle).toBeUndefined(); - expect(layoutOut.mapbox.layers[0].symbol).toBeUndefined(); - - expect(layoutOut.mapbox.layers[1].color).toEqual('blue'); - expect(layoutOut.mapbox.layers[1].fill.outlinecolor).toEqual('#d3d3d3'); - expect(layoutOut.mapbox.layers[1].line).toBeUndefined(); - expect(layoutOut.mapbox.layers[1].circle).toBeUndefined(); - expect(layoutOut.mapbox.layers[1].symbol).toBeUndefined(); - - expect(layoutOut.mapbox.layers[2].color).toEqual('green'); - expect(layoutOut.mapbox.layers[2].circle.radius).toEqual(20); - expect(layoutOut.mapbox.layers[2].line).toBeUndefined(); - expect(layoutOut.mapbox.layers[2].fill).toBeUndefined(); - expect(layoutOut.mapbox.layers[2].symbol).toBeUndefined(); - - expect(layoutOut.mapbox.layers[3].color).toEqual('yellow'); - expect(layoutOut.mapbox.layers[3].symbol.icon).toEqual('monument'); - expect(layoutOut.mapbox.layers[3].line).toBeUndefined(); - expect(layoutOut.mapbox.layers[3].fill).toBeUndefined(); - expect(layoutOut.mapbox.layers[3].circle).toBeUndefined(); - }); - - it('should not allow to set layer type other than *raster* for sourcetype value *raster* and *image*', function() { - spyOn(Lib, 'log'); - - layoutIn = { - mapbox: { - layers: [{ - sourcetype: 'raster', - source: 'url', - type: 'circle' - }, { - sourcetype: 'image', - source: 'url', - type: 'fill' - }] - } - }; - supplyLayoutDefaults(layoutIn, layoutOut, fullData); - - expect(Lib.log).toHaveBeenCalledTimes(2); - expect(Lib.log).toHaveBeenCalledWith('Source types *raster* and *image* must drawn *raster* layer type.'); - - expect(layoutOut.mapbox.layers[0].type).toBe('raster'); - expect(layoutOut.mapbox.layers[1].type).toBe('raster'); - }); - - it('should default layer with sourcetype *raster* and *image* to type *raster', function() { - spyOn(Lib, 'log'); - - layoutIn = { - mapbox: { - layers: [{ - sourcetype: 'raster', - source: 'url' - }, { - sourcetype: 'image', - source: 'url' - }] - } - }; - supplyLayoutDefaults(layoutIn, layoutOut, fullData); - - expect(Lib.log).toHaveBeenCalledTimes(0); - expect(layoutOut.mapbox.layers[0].type).toBe('raster'); - expect(layoutOut.mapbox.layers[1].type).toBe('raster'); - }); - - it('should set *layout.dragmode* to pan while zoom is not available', function() { - var gd = { - data: fullData, - layout: {} - }; - - supplyAllDefaults(gd); - expect(gd._fullLayout.dragmode).toBe('pan'); - }); -}); - -describe('mapbox credentials', function() { - var gd; - - var dummyToken = 'asfdsa124331wersdsa1321q3'; - - var osmStyle = { - id: 'osm', - version: 8, - sources: { - 'osm-tiles': { - type: 'raster', - tiles: [ - 'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png', - 'https://b.tile.openstreetmap.org/{z}/{x}/{y}.png' - ], - tileSize: 256 - } - }, - layers: [{ - id: 'osm-tiles', - type: 'raster', - source: 'osm-tiles', - minzoom: 0, - maxzoom: 22 - }] - }; - - beforeEach(function() { - gd = createGraphDiv(); - - Plotly.setPlotConfig({ - mapboxAccessToken: null - }); - }); - - afterEach(function() { - Plotly.purge(gd); - destroyGraphDiv(); - - Plotly.setPlotConfig({ - mapboxAccessToken: MAPBOX_ACCESS_TOKEN - }); - }); - - it('@gl should throw error when no non-mapbox style is set and missing a mapbox access token token', function() { - spyOn(Lib, 'error'); - - expect(function() { - Plotly.newPlot(gd, [{ - type: 'scattermapbox', - lon: [10, 20, 30], - lat: [10, 20, 30] - }]); - }).toThrow(new Error(constants.missingStyleErrorMsg)); - - expect(Lib.error).toHaveBeenCalledWith(constants.missingStyleErrorMsg); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should throw error when setting a Mapbox style w/o a registered token', function() { - spyOn(Lib, 'error'); - - expect(function() { - Plotly.newPlot(gd, [{ - type: 'scattermapbox', - lon: [10, 20, 30], - lat: [10, 20, 30] - }], { - mapbox: {style: 'basic'} - }); - }).toThrow(new Error(constants.noAccessTokenErrorMsg)); - - expect(Lib.error).toHaveBeenCalledWith('Uses Mapbox map style, but did not set an access token.'); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should throw error if token is invalid', function(done) { - var cnt = 0; - - Plotly.newPlot(gd, [{ - type: 'scattermapbox', - lon: [10, 20, 30], - lat: [10, 20, 30] - }], {}, { - mapboxAccessToken: dummyToken - }) - .catch(function(err) { - cnt++; - expect(err).toEqual(new Error(constants.mapOnErrorMsg)); - }) - .then(function() { - expect(cnt).toEqual(1); - done(); - }); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should use access token in mapbox layout options if present', function(done) { - var cnt = 0; - - Plotly.newPlot(gd, [{ - type: 'scattermapbox', - lon: [10, 20, 30], - lat: [10, 20, 30] - }], { - mapbox: { - accesstoken: MAPBOX_ACCESS_TOKEN - } - }, { - mapboxAccessToken: dummyToken - }).catch(function() { - cnt++; - }).then(function() { - expect(cnt).toEqual(0); - expect(gd._fullLayout.mapbox.accesstoken).toEqual(MAPBOX_ACCESS_TOKEN); - done(); - }); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should warn when multiple tokens in mapbox layout options are present', function(done) { - spyOn(Lib, 'warn'); - var cnt = 0; - - Plotly.newPlot(gd, [{ - type: 'scattermapbox', - lon: [10, 20, 30], - lat: [10, 20, 30] - }, { - type: 'scattermapbox', - lon: [10, 20, 30], - lat: [10, 20, 30], - subplot: 'mapbox2' - }], { - mapbox: { accesstoken: MAPBOX_ACCESS_TOKEN }, - mapbox2: { accesstoken: dummyToken } - }).catch(function() { - cnt++; - }).then(function() { - expect(cnt).toEqual(0); - expect(gd._fullLayout.mapbox.accesstoken).toEqual(MAPBOX_ACCESS_TOKEN); - expect(Lib.warn).toHaveBeenCalledWith(constants.multipleTokensErrorMsg); - done(); - }); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should not throw when using a custom non-mapbox style', function(done) { - var cnt = 0; - - Plotly.newPlot(gd, [{ - type: 'scattermapbox', - lon: [10, 20, 30], - lat: [10, 20, 30] - }], { - mapbox: { style: osmStyle } - }).catch(function() { - cnt++; - }).then(function() { - expect(cnt).toEqual(0); - expect(gd._fullLayout.mapbox.accesstoken).toBe(undefined); - done(); - }); - }, LONG_TIMEOUT_INTERVAL); - - it('@noCI @gl should not throw when using a custom mapbox style URL with an access token in the layout', function(done) { - var cnt = 0; - - Plotly.newPlot(gd, [{ - type: 'scattermapbox', - lon: [10, 20, 30], - lat: [10, 20, 30] - }], { - mapbox: { - accesstoken: MAPBOX_ACCESS_TOKEN, - style: 'mapbox://styles/plotly-js-tests/ck4og36lx0vnj1cpdl8y0cr8m' - } - }).catch(function() { - cnt++; - }).then(function() { - expect(cnt).toEqual(0); - expect(gd._fullLayout.mapbox.accesstoken).toBe(MAPBOX_ACCESS_TOKEN); - done(); - }); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should log when an access token is set while using a custom non-mapbox style', function(done) { - spyOn(Lib, 'log'); - var cnt = 0; - - Plotly.newPlot(gd, [{ - type: 'scattermapbox', - lon: [10, 20, 30], - lat: [10, 20, 30] - }], { - mapbox: { - style: osmStyle, - accesstoken: MAPBOX_ACCESS_TOKEN - } - }).catch(function() { - cnt++; - }).then(function() { - expect(cnt).toEqual(0); - expect(Lib.log).toHaveBeenCalledWith([ - 'Listed mapbox access token(s)', - MAPBOX_ACCESS_TOKEN, - 'but did not use a Mapbox map style, ignoring token(s).' - ].join(' ')); - done(); - }); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should bypass access token in mapbox layout options when config points to an Atlas server', function(done) { - var cnt = 0; - var msg = [ - 'An API access token is required to use Mapbox GL.', - 'See https://www.mapbox.com/api-documentation/#access-tokens-and-token-scopes' - ].join(' '); - - // TODO potential new way of doing this: - // https://github.com/mapbox/mapbox-gl-js/pull/7594 - // - // https://www.mapbox.com/atlas/#developing-with-atlas - - Plotly.newPlot(gd, [{ - type: 'scattermapbox', - lon: [10, 20, 30], - lat: [10, 20, 30] - }], { - mapbox: { - accesstoken: MAPBOX_ACCESS_TOKEN - } - }, { - mapboxAccessToken: '' - }) - .catch(function(err) { - cnt++; - // Note that we get an error here on `new mapboxgl.Map` - // as we don't have an Atlas server running. - // - // In essence, we test that the `new mapboxgl.Map` throws - // as oppose to `findAccessToken` - expect(err).toEqual(new Error(msg)); - }) - .then(function() { - expect(cnt).toEqual(1); - done(); - }); - }, LONG_TIMEOUT_INTERVAL); -}); - -describe('mapbox plots', function() { - var mock = require('../../image/mocks/mapbox_0.json'); - var gd; - - var pointPos = [579, 276]; - var blankPos = [650, 120]; - - beforeEach(function(done) { - gd = createGraphDiv(); - - var mockCopy = Lib.extendDeep({}, mock); - - Plotly.newPlot(gd, mockCopy.data, mockCopy.layout).then(done); - }); - - afterEach(function() { - Plotly.purge(gd); - destroyGraphDiv(); - }); - - it('@gl should be able to toggle trace visibility', function(done) { - var modes = ['line', 'circle']; - - expect(countVisibleTraces(gd, modes)).toEqual(2); - - Plotly.restyle(gd, 'visible', false).then(function() { - expect(gd._fullLayout.mapbox === undefined).toBe(false); - - expect(countVisibleTraces(gd, modes)).toEqual(0); - - return Plotly.restyle(gd, 'visible', true); - }) - .then(function() { - expect(countVisibleTraces(gd, modes)).toEqual(2); - - return Plotly.restyle(gd, 'visible', 'legendonly', [1]); - }) - .then(function() { - expect(countVisibleTraces(gd, modes)).toEqual(1); - - return Plotly.restyle(gd, 'visible', true); - }) - .then(function() { - expect(countVisibleTraces(gd, modes)).toEqual(2); - - var mockCopy = Lib.extendDeep({}, mock); - mockCopy.data[0].visible = false; - - return Plotly.newPlot(gd, mockCopy.data, mockCopy.layout); - }) - .then(function() { - expect(countVisibleTraces(gd, modes)).toEqual(1); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should be able to delete and add traces', function(done) { - var modes = ['line', 'circle']; - - expect(countVisibleTraces(gd, modes)).toEqual(2); - - Plotly.deleteTraces(gd, [0]).then(function() { - expect(countVisibleTraces(gd, modes)).toEqual(1); - - var trace = { - type: 'scattermapbox', - mode: 'markers+lines', - lon: [-10, -20, -10], - lat: [-10, 20, -10] - }; - - return Plotly.addTraces(gd, [trace]); - }) - .then(function() { - expect(countVisibleTraces(gd, modes)).toEqual(2); - - var trace = { - type: 'scattermapbox', - mode: 'markers+lines', - lon: [10, 20, 10], - lat: [10, -20, 10] - }; - - return Plotly.addTraces(gd, [trace]); - }) - .then(function() { - expect(countVisibleTraces(gd, modes)).toEqual(3); - - return Plotly.deleteTraces(gd, [0, 1, 2]); - }) - .then(function() { - expect(gd._fullLayout.mapbox === undefined).toBe(true); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should not update center while dragging', function(done) { - var map = gd._fullLayout.mapbox._subplot.map; - spyOn(map, 'setCenter').and.callThrough(); - - var p1 = [pointPos[0] + 50, pointPos[1] - 20]; - - _mouseEvent('mousemove', pointPos, noop).then(function() { - return Plotly.relayout(gd, {'mapbox.center': {lon: 13.5, lat: -19.5}}); - }).then(function() { - // First relayout on mapbox.center results in setCenter call - expect(map.setCenter).toHaveBeenCalledWith([13.5, -19.5]); - expect(map.setCenter).toHaveBeenCalledTimes(1); - }).then(function() { - return _mouseEvent('mousedown', pointPos, noop); - }).then(function() { - return _mouseEvent('mousemove', p1, noop); - }).then(function() { - return Plotly.relayout(gd, {'mapbox.center': {lat: 0, lon: 0}}); - }).then(function() { - return _mouseEvent('mouseup', p1, noop); - }).then(function() { - // Second relayout on mapbox.center does not result in a setCenter - // call since map drag is underway - expect(map.setCenter).toHaveBeenCalledTimes(1); - }).then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should not update zoom while scroll wheeling', function(done) { - var map = gd._fullLayout.mapbox._subplot.map; - spyOn(map, 'setZoom').and.callThrough(); - - _mouseEvent('mousemove', pointPos, noop).then(function() { - return Plotly.relayout(gd, {'mapbox.zoom': 5}); - }).then(function() { - // First relayout on mapbox.zoom results in setZoom call - expect(map.setZoom).toHaveBeenCalledWith(5); - expect(map.setZoom).toHaveBeenCalledTimes(1); - }).then(function() { - mouseEvent('scroll', pointPos[0], pointPos[1], {deltaY: -400}); - return Plotly.relayout(gd, {'mapbox.zoom': 2}).then(function() { - // Second relayout on mapbox.zoom does not result in setZoom - // call since a scroll wheel zoom is underway - expect(map.setZoom).toHaveBeenCalledTimes(1); - }); - }).then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should be able to restyle', function(done) { - var restyleCnt = 0; - var relayoutCnt = 0; - - gd.on('plotly_restyle', function() { - restyleCnt++; - }); - - gd.on('plotly_relayout', function() { - relayoutCnt++; - }); - - function assertMarkerColor(expectations) { - return new Promise(function(resolve) { - setTimeout(function() { - var objs = getStyle(gd, 'circle', 'circle-color'); - - expectations.forEach(function(expected, i) { - var obj = objs[i]; - var rgba = [obj.r, obj.g, obj.b, obj.a]; - expect(rgba).toBeCloseToArray(expected); - }); - - resolve(); - }, TRANSITION_DELAY); - }); - } - - assertMarkerColor([ - [0.121, 0.466, 0.705, 1], - [1, 0.498, 0.0549, 1] - ]) - .then(function() { - return Plotly.restyle(gd, 'marker.color', 'green'); - }) - .then(function() { - expect(restyleCnt).toEqual(1); - expect(relayoutCnt).toEqual(0); - - return assertMarkerColor([ - [0, 0.5019, 0, 1], - [0, 0.5019, 0, 1] - ]); - }) - .then(function() { - return Plotly.restyle(gd, 'marker.color', 'red', [1]); - }) - .then(function() { - expect(restyleCnt).toEqual(2); - expect(relayoutCnt).toEqual(0); - - return assertMarkerColor([ - [0, 0.5019, 0, 1], - [1, 0, 0, 1] - ]); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should be able to relayout', function(done) { - var restyleCnt = 0; - var relayoutCnt = 0; - - gd.on('plotly_restyle', function() { - restyleCnt++; - }); - - gd.on('plotly_relayout', function() { - relayoutCnt++; - }); - - function assertLayout(center, zoom, dims) { - var mapInfo = getMapInfo(gd); - - expect([mapInfo.center.lng, mapInfo.center.lat]) - .toBeCloseToArray(center); - expect(mapInfo.zoom).toBeCloseTo(zoom); - - var divStyle = mapInfo.div.style; - ['left', 'top', 'width', 'height'].forEach(function(p, i) { - expect(parseFloat(divStyle[p])).toBeWithin(dims[i], 8); - }); - } - - assertLayout([-4.710, 19.475], 1.234, [80, 100, 908, 270]); - - Plotly.relayout(gd, 'mapbox.center', { lon: 0, lat: 0 }) - .then(function() { - expect(restyleCnt).toEqual(0); - expect(relayoutCnt).toEqual(1); - - assertLayout([0, 0], 1.234, [80, 100, 908, 270]); - - return Plotly.relayout(gd, 'mapbox.zoom', '6'); - }) - .then(function() { - expect(restyleCnt).toEqual(0); - expect(relayoutCnt).toEqual(2); - - assertLayout([0, 0], 6, [80, 100, 908, 270]); - - return Plotly.relayout(gd, 'mapbox.domain.x', [0, 0.5]); - }) - .then(function() { - expect(restyleCnt).toEqual(0); - expect(relayoutCnt).toEqual(3); - - assertLayout([0, 0], 6, [80, 100, 454, 270]); - - return Plotly.relayout(gd, 'mapbox.domain.y[0]', 0.5); - }) - .then(function() { - expect(restyleCnt).toEqual(0); - expect(relayoutCnt).toEqual(4); - - assertLayout([0, 0], 6, [80, 100, 454, 135]); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should be able to relayout the map style', function(done) { - function assertLayout(style) { - var mapInfo = getMapInfo(gd); - expect(mapInfo.style.name).toEqual(style); - } - - // TODO - // this one now logs: - // 'Unable to perform style diff: Unimplemented: setSprite.. Rebuilding the style from scratch.' - // https://github.com/mapbox/mapbox-gl-js/issues/6933 - - assertLayout('Mapbox Dark'); - - Plotly.relayout(gd, 'mapbox.style', 'light') - .then(function() { - assertLayout('Mapbox Light'); - - return Plotly.relayout(gd, 'mapbox.style', 'dark'); - }) - .then(function() { - assertLayout('Mapbox Dark'); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should be able to add, update and remove layers', function(done) { - var mockWithLayers = require('../../image/mocks/mapbox_layers'); - - var layer0 = Lib.extendDeep({}, mockWithLayers.layout.mapbox.layers[0]); - var layer1 = Lib.extendDeep({}, mockWithLayers.layout.mapbox.layers[1]); - - var mapUpdate = { - 'mapbox.zoom': mockWithLayers.layout.mapbox.zoom, - 'mapbox.center.lon': mockWithLayers.layout.mapbox.center.lon, - 'mapbox.center.lat': mockWithLayers.layout.mapbox.center.lat - }; - - var styleUpdate0 = { - 'mapbox.layers[0].color': 'red', - 'mapbox.layers[0].fill.outlinecolor': 'blue', - 'mapbox.layers[0].opacity': 0.3 - }; - - var styleUpdate1 = { - 'mapbox.layers[1].color': 'blue', - 'mapbox.layers[1].line.width': 3, - 'mapbox.layers[1].opacity': 0.6 - }; - - function countVisibleLayers(gd) { - var mapInfo = getMapInfo(gd); - - var sourceLen = mapInfo.layoutSources.length; - var layerLen = mapInfo.layoutLayers.length; - - if(sourceLen !== layerLen) return null; - - return layerLen; - } - - function getLayerLength(gd) { - return Lib.filterVisible(gd._fullLayout.mapbox.layers || []).length; - } - - function assertLayerStyle(gd, expectations, index) { - var mapInfo = getMapInfo(gd); - var layers = mapInfo.layers; - var layerNames = mapInfo.layoutLayers; - - var layer = layers[layerNames[index]]; - expect(layer).toBeDefined(layerNames[index]); - - return new Promise(function(resolve) { - setTimeout(function() { - Object.keys(expectations).forEach(function(k) { - try { - var obj = layer.paint._values[k].value.value; - expect(String(obj)).toBe(String(expectations[k]), k); - } catch(e) { - fail('could not find paint values in layer'); - } - }); - resolve(); - }, TRANSITION_DELAY); - }); - } - - expect(countVisibleLayers(gd)).toEqual(0); - - Plotly.relayout(gd, 'mapbox.layers[0]', layer0) - .then(function() { - expect(getLayerLength(gd)).toEqual(1); - expect(countVisibleLayers(gd)).toEqual(1); - - // add a new layer at the beginning - return Plotly.relayout(gd, 'mapbox.layers[1]', layer1); - }) - .then(function() { - expect(getLayerLength(gd)).toEqual(2); - expect(countVisibleLayers(gd)).toEqual(2); - - // hide a layer - return Plotly.relayout(gd, 'mapbox.layers[0].visible', false); - }) - .then(function() { - expect(getLayerLength(gd)).toEqual(1); - expect(countVisibleLayers(gd)).toEqual(1); - - // re-show it - return Plotly.relayout(gd, 'mapbox.layers[0].visible', true); - }) - .then(function() { - expect(getLayerLength(gd)).toEqual(2); - expect(countVisibleLayers(gd)).toEqual(2); - - return Plotly.relayout(gd, mapUpdate); - }) - .then(function() { - expect(getLayerLength(gd)).toEqual(2); - expect(countVisibleLayers(gd)).toEqual(2); - - return Plotly.relayout(gd, styleUpdate0); - }) - .then(function() { - expect(getLayerLength(gd)).toEqual(2); - expect(countVisibleLayers(gd)).toEqual(2); - - return assertLayerStyle(gd, { - 'fill-color': 'rgba(255,0,0,1)', - 'fill-outline-color': 'rgba(0,0,255,1)', - 'fill-opacity': 0.3 - }, 0); - }) - .then(function() { - expect(getLayerLength(gd)).toEqual(2); - expect(countVisibleLayers(gd)).toEqual(2); - - return Plotly.relayout(gd, styleUpdate1); - }) - .then(function() { - expect(getLayerLength(gd)).toEqual(2); - expect(countVisibleLayers(gd)).toEqual(2); - - return assertLayerStyle(gd, { - 'line-width': 3, - 'line-color': 'rgba(0,0,255,1)', - 'line-opacity': 0.6 - }, 1); - }) - .then(function() { - expect(getLayerLength(gd)).toEqual(2); - expect(countVisibleLayers(gd)).toEqual(2); - - // delete the first layer - return Plotly.relayout(gd, 'mapbox.layers[0]', null); - }) - .then(function() { - expect(getLayerLength(gd)).toEqual(1); - expect(countVisibleLayers(gd)).toEqual(1); - - return Plotly.relayout(gd, 'mapbox.layers[0]', null); - }) - .then(function() { - expect(getLayerLength(gd)).toEqual(0); - expect(countVisibleLayers(gd)).toEqual(0); - - return Plotly.relayout(gd, 'mapbox.layers[0]', {}); - }) - .then(function() { - expect(gd.layout.mapbox.layers).toEqual([{}]); - expect(countVisibleLayers(gd)).toEqual(0); - - // layer with no source are not drawn - - return Plotly.relayout(gd, 'mapbox.layers[0].source', layer0.source); - }) - .then(function() { - expect(getLayerLength(gd)).toEqual(1); - expect(countVisibleLayers(gd)).toEqual(1); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should be able to update layer image', function(done) { - var coords = [ - [-80.425, 46.437], - [-71.516, 46.437], - [-71.516, 37.936], - [-80.425, 37.936] - ]; - function makeFigure(source) { - return { - data: [{type: 'scattermapbox'}], - layout: { - mapbox: { - layers: [{ - sourcetype: 'raster', - source: ['https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'], - below: 'traces', - }, { - sourcetype: 'image', - coordinates: coords, - source: source, - below: 'traces', - }], - } - } - }; - } - - var map = null; - var layerSource = null; - - // Single pixel PNGs generated with http://png-pixel.com/ - var prefix = 'data:image/png;base64,'; - var redImage = prefix + 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42m' + - 'P8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg=='; - var greenImage = prefix + 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42m' + - 'Nk+M/wHwAEBgIApD5fRAAAAABJRU5ErkJggg=='; - - Plotly.react(gd, makeFigure(redImage)).then(function() { - var mapbox = gd._fullLayout.mapbox._subplot; - map = mapbox.map; - layerSource = map.getSource(mapbox.layerList[1].idSource); - - spyOn(layerSource, 'updateImage').and.callThrough(); - spyOn(map, 'removeSource').and.callThrough(); - return Plotly.react(gd, makeFigure(greenImage)); - }) - .then(function() { - expect(layerSource.updateImage).toHaveBeenCalledWith( - {url: greenImage, coordinates: coords} - ); - expect(map.removeSource).not.toHaveBeenCalled(); - - // Check order of layers - var mapbox = gd._fullLayout.mapbox._subplot; - var mapboxLayers = mapbox.getMapLayers(); - var plotlyjsLayers = mapbox.layerList; - - var indexLower = mapboxLayers.findIndex(function(layer) { - return layer.id === 'plotly-layout-layer-' + plotlyjsLayers[0].uid; - }); - - var indexUpper = mapboxLayers.findIndex(function(layer) { - return layer.id === 'plotly-layout-layer-' + plotlyjsLayers[1].uid; - }); - - expect(indexLower).toBeGreaterThan(-1); - expect(indexUpper).toBeGreaterThan(0); - expect(indexUpper).toBe(indexLower + 1); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should be able to react to layer changes', function(done) { - function makeFigure(color) { - return { - data: [{type: 'scattermapbox'}], - layout: { - mapbox: { - layers: [{ - color: color, - sourcetype: 'geojson', - type: 'fill', - source: { - type: 'Feature', - properties: {}, - geometry: { - type: 'Polygon', - coordinates: [[ - [174.7447586059570, -36.86533886128865], - [174.7773742675781, -36.86533886128865], - [174.7773742675781, -36.84913134182603], - [174.7447586059570, -36.84913134182603], - [174.7447586059570, -36.86533886128865] - ]] - } - } - }] - } - } - }; - } - - function _assert(color) { - var mapInfo = getMapInfo(gd); - var layer = mapInfo.layers[mapInfo.layoutLayers[0]]; - - expect(mapInfo.layoutLayers.length).toBe(1, 'one layer'); - expect(mapInfo.layoutSources.length).toBe(1, 'one layer source'); - expect(String(layer.paint._values['fill-color'].value.value)).toBe(color, 'layer color'); - } - - // TODO - // this one now logs: - // 'Unable to perform style diff: Unimplemented: setSprite, setLayerProperty.. Rebuilding the style from scratch.' - // github.com/mapbox/mapbox-gl-js/issues/6933/ - - Plotly.react(gd, makeFigure('blue')).then(function() { - _assert('rgba(0,0,255,1)'); - return Plotly.react(gd, makeFigure('red')); - }) - .then(function() { - _assert('rgba(255,0,0,1)'); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should not wedge graph after reacting to invalid layer', function(done) { - Plotly.react(gd, [{type: 'scattermapbox'}], { - mapbox: { - layers: [{ source: 'invalid' }] - } - }) - .then(function() { - fail('The above Plotly.react promise should be rejected'); - }) - .catch(function() { - expect(gd._promises.length).toBe(1, 'has 1 rejected promise in queue'); - }) - .then(function() { - return Plotly.react(gd, [{type: 'scattermapbox'}], { - mapbox: { - layers: [{ - sourcetype: 'vector', - sourcelayer: 'contour', - source: 'mapbox://mapbox.mapbox-terrain-v2' - }] - } - }); - }) - .then(function() { - expect(gd._promises.length).toBe(0, 'rejected promise has been cleared'); - - var mapInfo = getMapInfo(gd); - expect(mapInfo.layoutLayers.length).toBe(1, 'one layer'); - expect(mapInfo.layoutSources.length).toBe(1, 'one layer source'); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should not attempt to remove non-existing layer sources', function(done) { - function _assert(msg, exp) { - return function() { - var layerList = gd._fullLayout.mapbox._subplot.layerList; - expect(layerList.length).toBe(exp, msg); - }; - } - - Plotly.react(gd, [{type: 'scattermapbox'}], { - mapbox: { layers: [{}] } - }) - .then(_assert('1 visible:false layer', 1)) - .then(function() { - return Plotly.react(gd, [{type: 'scattermapbox'}], { - mapbox: { layers: [] } - }); - }) - .then(_assert('no layers', 0)) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should validate layout layer input', function(done) { - Plotly.newPlot(gd, [{type: 'scattermapbox'}], { - mapbox: { - layers: [{ - sourcetype: 'raster', - source: [''] - }] - } - }) - .then(function() { - var mapInfo = getMapInfo(gd); - expect(mapInfo.layoutLayers.length).toBe(0, 'no on-map layer'); - expect(mapInfo.layoutSources.length).toBe(0, 'no map source'); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should be able to update the access token', function(done) { - Plotly.relayout(gd, 'mapbox.accesstoken', 'wont-work') - .catch(function(err) { - expect(gd._fullLayout.mapbox.accesstoken).toEqual('wont-work'); - expect(err).toEqual(new Error(constants.mapOnErrorMsg)); - expect(gd._promises.length).toEqual(1); - - return Plotly.relayout(gd, 'mapbox.accesstoken', MAPBOX_ACCESS_TOKEN); - }) - .then(function() { - expect(gd._fullLayout.mapbox.accesstoken).toEqual(MAPBOX_ACCESS_TOKEN); - expect(gd._promises.length).toEqual(0); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should be able to update traces', function(done) { - function assertDataPts(lengths) { - var lines = getGeoJsonData(gd, 'lines'); - var markers = getGeoJsonData(gd, 'markers'); - - lines.forEach(function(obj, i) { - expect(obj.coordinates[0].length).toEqual(lengths[i]); - }); - - markers.forEach(function(obj, i) { - expect(obj.features.length).toEqual(lengths[i]); - }); - } - - assertDataPts([3, 3]); - - var update = { - lon: [[10, 20]], - lat: [[-45, -20]] - }; - - Plotly.restyle(gd, update, [1]).then(function() { - assertDataPts([3, 2]); - - var update = { - lon: [ [10, 20], [30, 40, 20] ], - lat: [ [-10, 20], [10, 20, 30] ] - }; - - return Plotly.extendTraces(gd, update, [0, 1]); - }) - .then(function() { - assertDataPts([5, 5]); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should display to hover labels on mouse over', function(done) { - function assertMouseMove(pos, len) { - return _mouseEvent('mousemove', pos, function() { - var hoverLabels = d3Select('.hoverlayer').selectAll('g'); - - expect(hoverLabels.size()).toEqual(len); - }); - } - - assertMouseMove(blankPos, 0).then(function() { - return assertMouseMove(pointPos, 1); - }) - .then(function() { - return Plotly.restyle(gd, { - 'hoverlabel.bgcolor': 'yellow', - 'hoverlabel.font.size': [[20, 10, 30]] - }); - }) - .then(function() { - return assertMouseMove(pointPos, 1); - }) - .then(function() { - assertHoverLabelStyle(d3Select('g.hovertext'), { - bgcolor: 'rgb(255, 255, 0)', - bordercolor: 'rgb(68, 68, 68)', - fontSize: 20, - fontFamily: 'Arial', - fontColor: 'rgb(68, 68, 68)' - }); - assertHoverLabelContent({ - nums: '(10°, 10°)', - name: 'trace 0' - }); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should respond to hover interactions by', function(done) { - var hoverCnt = 0; - var unhoverCnt = 0; - - var hoverData, unhoverData; - - gd.on('plotly_hover', function(eventData) { - hoverCnt++; - hoverData = eventData.points[0]; - }); - - gd.on('plotly_unhover', function(eventData) { - unhoverCnt++; - unhoverData = eventData.points[0]; - }); - - _mouseEvent('mousemove', blankPos, function() { - expect(hoverData).toBe(undefined, 'not firing on blank points'); - expect(unhoverData).toBe(undefined, 'not firing on blank points'); - }) - .then(function() { - return _mouseEvent('mousemove', pointPos, function() { - expect(hoverData).not.toBe(undefined, 'firing on data points'); - expect(Object.keys(hoverData).sort()).toEqual(SORTED_EVENT_KEYS, 'returning the correct event data keys'); - expect(hoverData.curveNumber).toEqual(0, 'returning the correct curve number'); - expect(hoverData.pointNumber).toEqual(0, 'returning the correct point number'); - }); - }) - .then(function() { - return _mouseEvent('mousemove', blankPos, function() { - expect(unhoverData).not.toBe(undefined, 'firing on data points'); - expect(Object.keys(unhoverData).sort()).toEqual(SORTED_EVENT_KEYS, 'returning the correct event data keys'); - expect(unhoverData.curveNumber).toEqual(0, 'returning the correct curve number'); - expect(unhoverData.pointNumber).toEqual(0, 'returning the correct point number'); - }); - }) - .then(function() { - expect(hoverCnt).toEqual(1); - expect(unhoverCnt).toEqual(1); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should not attempt to rehover over exiting subplots', function(done) { - spyOn(Fx, 'hover').and.callThrough(); - - function countHoverLabels() { - return d3Select('.hoverlayer').selectAll('g').size(); - } - - Promise.resolve() - .then(function() { - return _mouseEvent('mousemove', pointPos, function() { - expect(countHoverLabels()).toEqual(1); - expect(Fx.hover).toHaveBeenCalledTimes(1); - expect(Fx.hover.calls.argsFor(0)[2]).toBe('mapbox'); - Fx.hover.calls.reset(); - }); - }) - .then(function() { return Plotly.deleteTraces(gd, [0, 1]); }) - .then(delay(10)) - .then(function() { - return _mouseEvent('mousemove', pointPos, function() { - expect(countHoverLabels()).toEqual(0); - // N.B. no additional calls from Plots.rehover() - // (as 'mapbox' subplot is gone), - // just one on the fallback xy subplot - expect(Fx.hover).toHaveBeenCalledTimes(1); - expect(Fx.hover.calls.argsFor(0)[2]).toBe('xy'); - }); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - - it('@noCI @gl should respond drag / scroll / double-click interactions', function(done) { - var relayoutCnt = 0; - var doubleClickCnt = 0; - var relayoutingCnt = 0; - var evtData; - - gd.on('plotly_relayout', function(d) { - relayoutCnt++; - evtData = d; - }); - - gd.on('plotly_relayouting', function() { - relayoutingCnt++; - }); - - gd.on('plotly_doubleclick', function() { - doubleClickCnt++; - }); - - function _scroll(p) { - return new Promise(function(resolve) { - mouseEvent('mousemove', p[0], p[1]); - mouseEvent('scroll', p[0], p[1], {deltaY: -400}); - setTimeout(resolve, 1000); - }); - } - - function _assertLayout(center, zoom) { - var mapInfo = getMapInfo(gd); - var layout = gd.layout.mapbox; - - expect([mapInfo.center.lng, mapInfo.center.lat]).toBeCloseToArray(center); - expect(mapInfo.zoom).toBeCloseTo(zoom); - - expect([layout.center.lon, layout.center.lat]).toBeCloseToArray(center); - expect(layout.zoom).toBeCloseTo(zoom); - } - - function _assert(center, zoom, lon0, lat0, lon1, lat1) { - _assertLayout(center, zoom); - - expect([evtData['mapbox.center'].lon, evtData['mapbox.center'].lat]).toBeCloseToArray(center); - expect(evtData['mapbox.zoom']).toBeCloseTo(zoom); - expect(Object.keys(evtData['mapbox._derived'])).toEqual(['coordinates']); - expect(evtData['mapbox._derived'].coordinates).toBeCloseTo2DArray([ - [lon0, lat1], - [lon1, lat1], - [lon1, lat0], - [lon0, lat0] - ], -0.1); - } - - _assertLayout([-4.710, 19.475], 1.234); - - var p1 = [pointPos[0] + 50, pointPos[1] - 20]; - - _drag(pointPos, p1, function() { - expect(relayoutCnt).toBe(1, 'relayout cnt'); - expect(relayoutingCnt).toBe(1, 'relayouting cnt'); - expect(doubleClickCnt).toBe(0, 'double click cnt'); - _assert([-19.651, 13.751], 1.234, - -155.15981291032617, -25.560300274373148, - 115.85734493011842, 47.573988219006424); - - return _doubleClick(p1); - }) - .then(function() { - expect(relayoutCnt).toBe(2, 'relayout cnt'); - expect(relayoutingCnt).toBe(1, 'relayouting cnt'); - expect(doubleClickCnt).toBe(1, 'double click cnt'); - _assert([-4.710, 19.475], 1.234, - -140.21950652441467, -20.054298691163496, - 130.79765131602989, 51.4513888208798); - - return _scroll(pointPos); - }) - .then(function() { - expect(relayoutCnt).toBe(3, 'relayout cnt'); - expect(relayoutingCnt).toBeCloseTo(10, -1, 'relayouting cnt'); - expect(doubleClickCnt).toBe(1, 'double click cnt'); - expect(getMapInfo(gd).zoom).toBeGreaterThan(1.234); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should respond to click interactions by', function(done) { - var ptData; - - gd.on('plotly_click', function(eventData) { - ptData = eventData.points[0]; - }); - - Promise.resolve() - .then(function() { return click(blankPos[0], blankPos[1]); }) - .then(delay(100)) - .then(function() { - expect(ptData).toBe(undefined, 'not firing on blank points'); - }) - .then(delay(100)) - .then(function() { return click(pointPos[0], pointPos[1]); }) - .then(function() { - expect(ptData).not.toBe(undefined, 'firing on data points'); - expect(Object.keys(ptData).sort()).toEqual(SORTED_EVENT_KEYS, 'returning the correct event data keys'); - expect(ptData.curveNumber).toEqual(0, 'returning the correct curve number'); - expect(ptData.pointNumber).toEqual(0, 'returning the correct point number'); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - - it('@noCI @gl should respect scrollZoom config option', function(done) { - var mockCopy2 = Lib.extendDeep({}, mock); - mockCopy2.config = {scrollZoom: false}; - - var relayoutCnt = 0; - var addOnGd = function() { - gd.on('plotly_relayout', function() { relayoutCnt++; }); - }; - - function _scroll() { - relayoutCnt = 0; - return new Promise(function(resolve) { - mouseEvent('mousemove', pointPos[0], pointPos[1]); - mouseEvent('scroll', pointPos[0], pointPos[1], {deltaY: -400}); - setTimeout(resolve, 500); - }); - } - - var zoom = getMapInfo(gd).zoom; - expect(zoom).toBeCloseTo(1.234); - var zoom0 = zoom; - - addOnGd(); - - _scroll().then(function() { - expect(relayoutCnt).toBe(1, 'scroll relayout cnt'); - - var zoomNew = getMapInfo(gd).zoom; - expect(zoomNew).toBeGreaterThan(zoom); - zoom = zoomNew; - }) - .then(function() { return Plotly.newPlot(gd, gd.data, gd.layout, {scrollZoom: false}); }) - .then(addOnGd) - .then(_scroll) - .then(function() { - expect(relayoutCnt).toBe(0, 'no additional relayout call'); - - var zoomNew = getMapInfo(gd).zoom; - expect(zoomNew).toBe(zoom); - zoom = zoomNew; - }) - .then(function() { return Plotly.newPlot(gd, gd.data, gd.layout, {scrollZoom: true}); }) - .then(addOnGd) - .then(_scroll) - .then(function() { - expect(relayoutCnt).toBe(1, 'scroll relayout cnt'); - - var zoomNew = getMapInfo(gd).zoom; - expect(zoomNew).toBeGreaterThan(zoom); - }) - .then(function() { return Plotly.newPlot(gd, mockCopy2); }) - .then(addOnGd) - .then(_scroll) - .then(function() { - // see https://github.com/plotly/plotly.js/issues/3738 - var zoomNew = getMapInfo(gd).zoom; - expect(zoomNew).toBe(zoom0); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - - describe('attributions', function() { - function assertLinks(s, exp) { - var elements = s[0][0].getElementsByTagName('a'); - expect(elements.length).toEqual(exp.length); - for(var i = 0; i < elements.length; i++) { - var e = elements[i]; - expect(e.href).toEqual(exp[i]); - expect(e.target).toEqual('_blank'); - } - } - - it('@gl should be displayed for style "Carto"', function(done) { - Plotly.newPlot(gd, [{type: 'scattermapbox'}], {mapbox: {style: 'carto-darkmatter'}}) - .then(function() { - var s = d3SelectAll('.mapboxgl-ctrl-attrib'); - expect(s.size()).toBe(1); - expect(s.text()).toEqual('© Carto © OpenStreetMap contributors'); - assertLinks(s, [ - 'https://carto.com/', - 'https://www.openstreetmap.org/copyright' - ]); - }) - .then(done, done.fail); - }); - - ['stamen-terrain', 'stamen-toner'].forEach(function(style) { - it('@noCI @gl should be displayed for style "' + style + '"', function(done) { - Plotly.newPlot(gd, [{type: 'scattermapbox'}], {mapbox: {style: style}}) - .then(function() { - var s = d3SelectAll('.mapboxgl-ctrl-attrib'); - expect(s.size()).toBe(1); - expect(s.text()).toEqual('Map tiles by Stamen Design under CC BY 3.0 | Data by OpenStreetMap contributors under ODbL'); - assertLinks(s, [ - 'https://stamen.com/', - 'https://creativecommons.org/licenses/by/3.0', - 'https://openstreetmap.org/', - 'https://www.openstreetmap.org/copyright' - ]); - }) - .then(done, done.fail); - }); - }); - - it('@noCI @gl should be displayed for style "stamen-watercolor"', function(done) { - Plotly.newPlot(gd, [{type: 'scattermapbox'}], {mapbox: {style: 'stamen-watercolor'}}) - .then(function() { - var s = d3SelectAll('.mapboxgl-ctrl-attrib'); - expect(s.size()).toBe(1); - expect(s.text()).toEqual('Map tiles by Stamen Design under CC BY 3.0 | Data by OpenStreetMap contributors under CC BY SA'); - assertLinks(s, [ - 'https://stamen.com/', - 'https://creativecommons.org/licenses/by/3.0', - 'https://openstreetmap.org/', - 'https://creativecommons.org/licenses/by-sa/3.0' - ]); - }) - .then(done, done.fail); - }); - - it('@gl should be displayed for style "open-street-map"', function(done) { - Plotly.newPlot(gd, [{type: 'scattermapbox'}], {mapbox: {style: 'open-street-map'}}) - .then(function() { - var s = d3SelectAll('.mapboxgl-ctrl-attrib'); - expect(s.size()).toBe(1); - expect(s.text()).toEqual('© OpenStreetMap contributors'); - assertLinks(s, [ - 'https://www.openstreetmap.org/copyright' - ]); - }) - .then(done, done.fail); - }); - - it('@gl should be displayed for style from Mapbox', function(done) { - Plotly.newPlot(gd, [{type: 'scattermapbox'}], {mapbox: {style: 'basic'}}) - .then(function() { - var s = d3SelectAll('.mapboxgl-ctrl-attrib'); - expect(s.size()).toBe(1); - expect(s.text()).toEqual('© Mapbox © OpenStreetMap Improve this map'); - assertLinks(s, [ - 'https://www.mapbox.com/about/maps/', - 'https://www.openstreetmap.org/about/', - 'https://apps.mapbox.com/feedback/?owner=mapbox&id=basic-v9&access_token=' + MAPBOX_ACCESS_TOKEN // Improve this map - ]); - }) - .then(done, done.fail); - }); - - function mockLayoutCustomStyle() { - return { - mapbox: { - style: { - id: 'osm', - version: 8, - sources: { - 'simple-tiles': { - type: 'raster', - tiles: [ - 'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png', - 'https://b.tile.openstreetmap.org/{z}/{x}/{y}.png' - ], - tileSize: 256 - } - }, - layers: [ - { - id: 'simple-tiles', - type: 'raster', - source: 'simple-tiles', - minzoom: 0, - maxzoom: 22 - } - ] - } - } - }; - } - - it('@gl should not be displayed for custom style without attribution', function(done) { - Plotly.newPlot(gd, [{type: 'scattermapbox'}], mockLayoutCustomStyle()) - .then(function() { - var s = d3SelectAll('.mapboxgl-ctrl-attrib'); - expect(s.size()).toBe(1); - expect(s.text()).toEqual(''); - }) - .then(done, done.fail); - }); - - it('@gl should be displayed for custom style with attribution', function(done) { - var attr = 'custom attribution'; - var layout = mockLayoutCustomStyle(); - layout.mapbox.style.sources['simple-tiles'].attribution = attr; - Plotly.newPlot(gd, [{type: 'scattermapbox'}], layout) - .then(function() { - var s = d3SelectAll('.mapboxgl-ctrl-attrib'); - expect(s.size()).toBe(1); - expect(s.text()).toEqual(attr); - }) - .then(done, done.fail); - }); - - it('@noCI @gl should be displayed for attributions defined in layers\' sourceattribution', function(done) { - var mock = require('../../image/mocks/mapbox_layers.json'); - var customMock = Lib.extendDeep(mock); - - var attr = 'custom attribution'; - var XSS = ''; - customMock.data.pop(); - customMock.layout.mapbox.layers[0].sourceattribution = XSS + attr; - - Plotly.newPlot(gd, customMock) - .then(function() { - var s = d3SelectAll('.mapboxgl-ctrl-attrib'); - expect(s.size()).toBe(1); - expect(s.text()).toEqual([XSS + attr, '© Mapbox © OpenStreetMap Improve this map'].join(' | ')); - expect(s.html().indexOf('')).toBe(-1); - expect(s.html().indexOf('<img src=x onerror="alert(XSS);">')).not.toBe(-1); - }) - .then(done, done.fail); - }); - }); - - function countVisibleTraces(gd, modes) { - var mapInfo = getMapInfo(gd); - var cnts = []; - - // 'modes' are the ScatterMapbox layers names - // e.g. 'fill', 'line', 'circle', 'symbol' - - modes.forEach(function(mode) { - var cntPerMode = 0; - - mapInfo.traceLayers.forEach(function(l) { - var info = mapInfo.layers[l]; - - if(l.indexOf(mode) === -1) return; - if(info.visibility === 'visible') cntPerMode++; - }); - - cnts.push(cntPerMode); - }); - - var cnt = cnts.reduce(function(a, b) { - return (a === b) ? a : null; - }); - - // returns null if not all counter per mode are the same, - // returns the counter if all are the same. - - return cnt; - } - - function getStyle(gd, mode, prop) { - var mapInfo = getMapInfo(gd); - var values = []; - - mapInfo.traceLayers.forEach(function(l) { - var info = mapInfo.layers[l]; - - if(l.indexOf(mode) === -1) return; - - values.push(info.paint._values[prop].value.value); - }); - - return values; - } - - function getGeoJsonData(gd, mode) { - var mapInfo = getMapInfo(gd); - var out = []; - - mapInfo.traceSources.forEach(function(s) { - var info = mapInfo.sources[s]; - - if(s.indexOf(mode) === -1) return; - - out.push(info._data); - }); - - return out; - } - - function _mouseEvent(type, pos, cb) { - return new Promise(function(resolve) { - mouseEvent(type, pos[0], pos[1], { - buttons: 1 // left button - }); - - setTimeout(function() { - cb(); - resolve(); - }, MOUSE_DELAY); - }); - } - - function _doubleClick(pos) { - return _mouseEvent('dblclick', pos, noop); - } - - function _drag(p0, p1, cb) { - var promise = _mouseEvent('mousemove', p0, noop).then(function() { - return _mouseEvent('mousedown', p0, noop); - }).then(function() { - return _mouseEvent('mousemove', p1, noop); - }).then(function() { - // repeat mousemove to simulate long dragging motion - return _mouseEvent('mousemove', p1, noop); - }).then(function() { - return _mouseEvent('mouseup', p1, noop); - }).then(function() { - return _mouseEvent('mouseup', p1, noop); - }).then(cb); - - return promise; - } -}); - -describe('mapbox react', function() { - var gd; - - beforeEach(function() { - gd = createGraphDiv(); - }); - - afterEach(function() { - Plotly.purge(gd); - destroyGraphDiv(); - }); - - it('@gl should be able to react to new tiles', function(done) { - function assertTile(link) { - var mapInfo = getMapInfo(gd); - expect(mapInfo.style.sources.REF.tiles[0]).toEqual(link); - } - - var firstLink = 'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'; - var secondLink = 'https://a.tile.stamen.com/watercolor/{z}/{x}/{y}.jpg'; - - var fig = { - data: [ - { - type: 'scattermapbox', - lon: [10, 20], - lat: [20, 10] - } - ], - layout: { - mapbox: { - style: { - version: 8, - sources: { - REF: { - type: 'raster', - tileSize: 256, - tiles: [firstLink] - } - }, - layers: [{ - id: 'REF', - source: 'REF', - type: 'raster' - }], - } - } - } - }; - - Plotly.newPlot(gd, fig) - .then(function() { - assertTile(firstLink); - - // copy figure - var newFig = JSON.parse(JSON.stringify(fig)); - - // new figure - newFig.layout.mapbox.style.sources = { - REF: { - type: 'raster', - tileSize: 256, - tiles: [secondLink] - } - }; - - // update - Plotly.react(gd, newFig); - }) - .then(function() { - assertTile(secondLink); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); -}); - -describe('test mapbox trace/layout *below* interactions', function() { - var gd; - - beforeEach(function() { - gd = createGraphDiv(); - }); - - afterEach(function(done) { - Plotly.purge(gd); - destroyGraphDiv(); - setTimeout(done, 200); - }); - - function getLayerIds() { - var subplot = gd._fullLayout.mapbox._subplot; - var layers = subplot.map.getStyle().layers; - var layerIds = layers.map(function(l) { return l.id; }); - return layerIds; - } - - it('@gl should be able to update *below* - scattermapbox + layout layer case', function(done) { - function _assert(msg, exp) { - var layersIds = getLayerIds(); - var tracePrefix = 'plotly-trace-layer-' + gd._fullData[0].uid; - - expect(layersIds.indexOf(tracePrefix + '-fill')).toBe(exp.trace[0], msg + '| fill'); - expect(layersIds.indexOf(tracePrefix + '-line')).toBe(exp.trace[1], msg + '| line'); - expect(layersIds.indexOf(tracePrefix + '-circle')).toBe(exp.trace[2], msg + '| circle'); - expect(layersIds.indexOf(tracePrefix + '-symbol')).toBe(exp.trace[3], msg + '| symbol'); - - var layoutLayerId = ['plotly-layout-layer', gd._fullLayout._uid, 'mapbox-0'].join('-'); - expect(layersIds.indexOf(layoutLayerId)).toBe(exp.layout, msg + '| layout layer'); - } - - Plotly.newPlot(gd, [{ - type: 'scattermapbox', - lon: [10, 20, 30], - lat: [15, 25, 35], - uid: 'a' - }], { - mapbox: { - style: 'basic', - layers: [{ - sourcetype: 'vector', - source: 'mapbox://mapbox.mapbox-terrain-v2', - sourcelayer: 'contour', - type: 'line' - }] - } - }) - .then(function() { - _assert('default *below*', { - trace: [20, 21, 22, 23], - layout: 24 - }); - }) - .then(function() { return Plotly.relayout(gd, 'mapbox.layers[0].below', 'traces'); }) - .then(function() { - _assert('with layout layer *below:traces*', { - trace: [21, 22, 23, 24], - layout: 20 - }); - }) - .then(function() { return Plotly.relayout(gd, 'mapbox.layers[0].below', null); }) - .then(function() { - _assert('back to default *below* (1)', { - trace: [20, 21, 22, 23], - layout: 24 - }); - }) - .then(function() { return Plotly.restyle(gd, 'below', ''); }) - .then(function() { - _assert('with trace *below:""*', { - trace: [21, 22, 23, 24], - layout: 20 - }); - }) - .then(function() { return Plotly.restyle(gd, 'below', null); }) - .then(function() { - _assert('back to default *below* (2)', { - trace: [20, 21, 22, 23], - layout: 24 - }); - }) - .then(function() { return Plotly.restyle(gd, 'below', 'water'); }) - .then(function() { - _assert('with trace *below:water*', { - trace: [4, 5, 6, 7], - layout: 24 - }); - }) - .then(function() { return Plotly.relayout(gd, 'mapbox.layers[0].below', 'water'); }) - .then(function() { - _assert('with trace AND layout layer *below:water*', { - trace: [4, 5, 6, 7], - layout: 8 - }); - }) - .then(function() { return Plotly.relayout(gd, 'mapbox.layers[0].below', ''); }) - .then(function() { - _assert('with trace *below:water* and layout layer *below:""*', { - trace: [4, 5, 6, 7], - layout: 24 - }); - }) - .then(function() { return Plotly.restyle(gd, 'below', ''); }) - .then(function() { - _assert('with trace AND layout layer *below:water*', { - trace: [20, 21, 22, 23], - layout: 24 - }); - }) - .then(function() { return Plotly.update(gd, {below: null}, {'mapbox.layers[0].below': null}); }) - .then(function() { - _assert('back to default *below* (3)', { - trace: [20, 21, 22, 23], - layout: 24 - }); - }) - .then(done, done.fail); - }, 8 * jasmine.DEFAULT_TIMEOUT_INTERVAL); - - it('@gl should be able to update *below* - scattermapbox + choroplethmapbox + densitymapbox case', function(done) { - function _assert(msg, exp) { - var layersIds = getLayerIds(); - var tracePrefix = 'plotly-trace-layer-'; - - var scatterPrefix = tracePrefix + 'scatter'; - expect(layersIds.indexOf(scatterPrefix + '-fill')).toBe(exp.scatter[0], msg + '| scatter fill'); - expect(layersIds.indexOf(scatterPrefix + '-line')).toBe(exp.scatter[1], msg + '| scatter line'); - expect(layersIds.indexOf(scatterPrefix + '-circle')).toBe(exp.scatter[2], msg + '| scatter circle'); - expect(layersIds.indexOf(scatterPrefix + '-symbol')).toBe(exp.scatter[3], msg + '| scatter symbol'); - - var densityPrefix = tracePrefix + 'density'; - expect(layersIds.indexOf(densityPrefix + '-heatmap')).toBe(exp.density[0], msg + '| density heatmap'); - - var choroplethPrefix = tracePrefix + 'choropleth'; - expect(layersIds.indexOf(choroplethPrefix + '-fill')).toBe(exp.choropleth[0], msg + '| choropleth fill'); - expect(layersIds.indexOf(choroplethPrefix + '-line')).toBe(exp.choropleth[1], msg + '| choropleth line'); - } - - Plotly.newPlot(gd, [{ - type: 'scattermapbox', - lon: [10, 20, 30], - lat: [15, 25, 35], - uid: 'scatter' - }, { - type: 'densitymapbox', - lon: [10, 20, 30], - lat: [15, 25, 35], - z: [1, 20, 5], - uid: 'density' - }, { - type: 'choroplethmapbox', - geojson: 'https://raw.githubusercontent.com/python-visualization/folium/master/examples/data/us-states.json', - locations: ['AL'], - z: [10], - uid: 'choropleth' - }], { - mapbox: {style: 'basic'} - }) - .then(function() { - _assert('base', { - scatter: [23, 24, 25, 26], - density: [17], - choropleth: [5, 6] - }); - }) - .then(function() { return Plotly.restyle(gd, 'below', ''); }) - .then(function() { - _assert('all traces *below:""', { - scatter: [23, 24, 25, 26], - density: [22], - choropleth: [20, 21] - }); - }) - .then(function() { return Plotly.restyle(gd, 'below', null); }) - .then(function() { - _assert('back to base', { - scatter: [23, 24, 25, 26], - density: [17], - choropleth: [5, 6] - }); - }) - .then(done, done.fail); - }, 8 * jasmine.DEFAULT_TIMEOUT_INTERVAL); - - it('@gl should be warn when *below* value does not correspond to a layer on the map', function(done) { - spyOn(Lib, 'warn'); - - var notGonnaWork = 'not-gonna-work'; - var arg = [ - 'Trying to add layer with *below* value', - notGonnaWork, - 'referencing a layer that does not exist', - 'or that does not yet exist.' - ].join(' '); - - function _assertFallback(msg, exp) { - var allArgs = Lib.warn.calls.allArgs(); - - if(allArgs.length === exp.warnCnt) { - for(var i = 0; i < exp.warnCnt; i++) { - expect(allArgs[i][0]).toBe(arg, 'Lib.warn call #' + i); - } - } else { - fail('Incorrect number of Lib.warn calls'); - } - Lib.warn.calls.reset(); - - getLayerIds().slice(20, -1).forEach(function(id) { - expect(id.indexOf('plotly-')).toBe(0, 'layer ' + id + ' fallback to top of map'); - }); - } - - Plotly.newPlot(gd, [{ - type: 'scattermapbox', - lon: [10, 20, 30], - lat: [15, 25, 35] - }, { - type: 'densitymapbox', - lon: [10, 20, 30], - lat: [15, 25, 35], - z: [1, 20, 5] - }, { - type: 'choroplethmapbox', - geojson: 'https://raw.githubusercontent.com/python-visualization/folium/master/examples/data/us-states.json', - locations: ['AL'], - z: [10] - }], { - mapbox: { - style: 'basic', - layers: [{ - sourcetype: 'vector', - source: 'mapbox://mapbox.mapbox-terrain-v2', - sourcelayer: 'contour', - type: 'line' - }] - } - }) - .then(function() { - expect(Lib.warn).toHaveBeenCalledTimes(0); - }) - .then(function() { return Plotly.restyle(gd, 'below', notGonnaWork); }) - .then(function() { - // 7 for 4 scattermapbox + 2 choroplethmapbox + 1 densitymapbox layer - _assertFallback('not-gonna-work for traces', {warnCnt: 7}); - }) - .then(function() { return Plotly.relayout(gd, 'mapbox.layers[0].below', 'not-gonna-work'); }) - .then(function() { - // same as last but + layout layer - _assertFallback('not-gonna-work for traces', {warnCnt: 8}); - }) - .then(function() { return Plotly.update(gd, {below: null}, {'mapbox.layers[0].below': null}); }) - .then(function() { - expect(Lib.warn).toHaveBeenCalledTimes(0); - }) - .then(done, done.fail); - }, 8 * jasmine.DEFAULT_TIMEOUT_INTERVAL); -}); - -describe('Test mapbox GeoJSON fetching:', function() { - var gd; - - beforeEach(function() { - gd = createGraphDiv(); - }); - - afterEach(function(done) { - Plotly.purge(gd); - destroyGraphDiv(); - setTimeout(done, 200); - }); - - it('@gl should fetch GeoJSON using URLs found in the traces', function(done) { - var url = 'https://raw.githubusercontent.com/plotly/datasets/master/florida-red-data.json'; - var url2 = 'https://raw.githubusercontent.com/plotly/datasets/master/florida-blue-data.json'; - var cnt = 0; - - Plotly.newPlot(gd, [{ - type: 'choroplethmapbox', - locations: ['a'], - z: [1], - geojson: url - }, { - type: 'choroplethmapbox', - locations: ['a'], - z: [1], - geojson: url2 - }]) - .catch(function() { - cnt++; - }) - .then(function() { - expect(cnt).toBe(0, 'no failures!'); - expect(Lib.isPlainObject(window.PlotlyGeoAssets[url])).toBe(true, 'is a GeoJSON object'); - expect(Lib.isPlainObject(window.PlotlyGeoAssets[url2])).toBe(true, 'is a GeoJSON object'); - }) - .then(done, done.fail); - }); - - it('@gl should fetch GeoJSON using URLs found in the traces', function(done) { - var actual = ''; - - Plotly.newPlot(gd, [{ - type: 'choroplethmapbox', - locations: ['a'], - z: [1], - geojson: 'invalidUrl' - }, { - type: 'choroplethmapbox', - locations: ['a'], - z: [1], - geojson: 'invalidUrl-two' - }]) - .catch(function(reason) { - // bails up after first failure - actual = reason; - }) - .then(function() { - expect(actual).toEqual(new Error('GeoJSON at URL "invalidUrl" does not exist.')); - expect(window.PlotlyGeoAssets.invalidUrl).toBe(undefined); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); -}); - -describe('mapbox toImage', function() { - // decreased from 1e5 - perhaps chrome got better at encoding these - // because I get 99330 and the image still looks correct - var MINIMUM_LENGTH = 7e4; - - var gd; - - beforeEach(function() { - gd = createGraphDiv(); - }); - - afterEach(function() { - Plotly.purge(gd); - Plotly.setPlotConfig({ mapboxAccessToken: null }); - destroyGraphDiv(); - }); - - it('@gl should generate image data with global credentials', function(done) { - Plotly.setPlotConfig({ - mapboxAccessToken: MAPBOX_ACCESS_TOKEN - }); - - Plotly.newPlot(gd, [{ - type: 'scattermapbox', - lon: [0, 10, 20], - lat: [-10, 10, -10] - }]) - .then(function() { - return Plotly.toImage(gd); - }) - .then(function(imgData) { - expect(imgData.length).toBeGreaterThan(MINIMUM_LENGTH); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should generate image data with config credentials', function(done) { - Plotly.newPlot(gd, [{ - type: 'scattermapbox', - lon: [0, 10, 20], - lat: [-10, 10, -10] - }], {}, { - mapboxAccessToken: MAPBOX_ACCESS_TOKEN - }) - .then(function() { - return Plotly.toImage(gd); - }) - .then(function(imgData) { - expect(imgData.length).toBeGreaterThan(MINIMUM_LENGTH); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - - it('@gl should generate image data with layout credentials', function(done) { - Plotly.newPlot(gd, [{ - type: 'scattermapbox', - lon: [0, 10, 20], - lat: [-10, 10, -10] - }], { - mapbox: { - accesstoken: MAPBOX_ACCESS_TOKEN - } - }) - .then(function() { - return Plotly.toImage(gd); - }) - .then(function(imgData) { - expect(imgData.length).toBeGreaterThan(MINIMUM_LENGTH); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); -}); - -function getMapInfo(gd) { - var subplot = gd._fullLayout.mapbox._subplot; - var map = subplot.map; - - var sources = map.style.sourceCaches; - var layers = map.style._layers; - var uid = subplot.uid; - - var traceSources = Object.keys(sources).filter(function(k) { - return k.indexOf('source-') === 0; - }); - - var traceLayers = Object.keys(layers).filter(function(k) { - return k.indexOf('plotly-trace-layer-') === 0; - }); - - var layoutSources = Object.keys(sources).filter(function(k) { - return k.indexOf(uid) !== -1; - }); - - var layoutLayers = Object.keys(layers).filter(function(k) { - return k.indexOf(uid) !== -1; - }); - - return { - map: map, - div: subplot.div, - sources: sources, - layers: layers, - traceSources: traceSources, - traceLayers: traceLayers, - layoutSources: layoutSources, - layoutLayers: layoutLayers, - center: map.getCenter(), - zoom: map.getZoom(), - style: map.getStyle() - }; -} diff --git a/test/jasmine/tests/plot_api_react_test.js b/test/jasmine/tests/plot_api_react_test.js index e858a52e8ad..657127d6734 100644 --- a/test/jasmine/tests/plot_api_react_test.js +++ b/test/jasmine/tests/plot_api_react_test.js @@ -876,15 +876,6 @@ describe('@noCIdep Plotly.react', function() { }); }); - mockLists.mapbox.forEach(function(mockSpec) { - it('@noCI @gl can redraw "' + mockSpec[0] + '" with no changes as a noop (mapbox mocks)', function(done) { - Plotly.setPlotConfig({ - mapboxAccessToken: MAPBOX_ACCESS_TOKEN - }); - _runReactMock(mockSpec, done); - }); - }); - mockLists.map.forEach(function(mockSpec) { it('@noCI @gl can redraw "' + mockSpec[0] + '" with no changes as a noop (map mocks)', function(done) { Plotly.setPlotConfig({}); diff --git a/test/jasmine/tests/scattermapbox_test.js b/test/jasmine/tests/scattermapbox_test.js deleted file mode 100644 index aa07efcd5c8..00000000000 --- a/test/jasmine/tests/scattermapbox_test.js +++ /dev/null @@ -1,1299 +0,0 @@ -var Plotly = require('../../../lib/index'); -var Plots = require('../../../src/plots/plots'); -var Lib = require('../../../src/lib'); -var Axes = require('../../../src/plots/cartesian/axes'); - -var ScatterMapbox = require('../../../src/traces/scattermapbox'); -var convert = require('../../../src/traces/scattermapbox/convert'); - -var createGraphDiv = require('../assets/create_graph_div'); -var destroyGraphDiv = require('../assets/destroy_graph_div'); - -var supplyAllDefaults = require('../assets/supply_defaults'); - -var assertHoverLabelContent = require('../assets/custom_assertions').assertHoverLabelContent; -var mouseEvent = require('../assets/mouse_event'); -var click = require('../assets/click'); -var HOVERMINTIME = require('../../../src/components/fx').constants.HOVERMINTIME; - -function move(fromX, fromY, toX, toY, delay) { - return new Promise(function(resolve) { - mouseEvent('mousemove', fromX, fromY); - - setTimeout(function() { - mouseEvent('mousemove', toX, toY); - resolve(); - }, delay || HOVERMINTIME + 10); - }); -} - -describe('scattermapbox defaults', function() { - 'use strict'; - - function _supply(traceIn) { - var traceOut = { visible: true }; - var defaultColor = '#444'; - var layout = { _dataLength: 1 }; - - ScatterMapbox.supplyDefaults(traceIn, traceOut, defaultColor, layout); - - return traceOut; - } - - it('should not truncate \'lon\' if longer than \'lat\'', function() { - // this is handled at the calc step now via _length. - var fullTrace = _supply({ - lon: [1, 2, 3], - lat: [2, 3] - }); - - expect(fullTrace.lon).toEqual([1, 2, 3]); - expect(fullTrace.lat).toEqual([2, 3]); - expect(fullTrace._length).toBe(2); - }); - - it('should not truncate \'lat\' if longer than \'lon\'', function() { - // this is handled at the calc step now via _length. - var fullTrace = _supply({ - lon: [1, 2, 3], - lat: [2, 3, 3, 5] - }); - - expect(fullTrace.lon).toEqual([1, 2, 3]); - expect(fullTrace.lat).toEqual([2, 3, 3, 5]); - expect(fullTrace._length).toBe(3); - }); - - it('should set \'visible\' to false if \'lat\' and/or \'lon\' has zero length', function() { - var fullTrace = _supply({ - lon: [1, 2, 3], - lat: [] - }); - - expect(fullTrace.visible).toEqual(false); - - fullTrace = _supply({ - lon: null, - lat: [1, 2, 3] - }); - - expect(fullTrace.visible).toEqual(false); - }); - - it('should set \'marker.color\' and \'marker.size\' to first item if symbol is set to \'circle\'', function() { - var base = { - mode: 'markers', - lon: [1, 2, 3], - lat: [2, 3, 3], - marker: { - color: ['red', 'green', 'blue'], - size: [10, 20, 30] - } - }; - - var fullTrace = _supply(Lib.extendDeep({}, base, { - marker: { symbol: 'monument' } - })); - - expect(fullTrace.marker.color).toEqual('red'); - expect(fullTrace.marker.size).toEqual(10); - - fullTrace = _supply(Lib.extendDeep({}, base, { - marker: { symbol: ['monument', 'music', 'harbor'] } - })); - - expect(fullTrace.marker.color).toEqual('red'); - expect(fullTrace.marker.size).toEqual(10); - - fullTrace = _supply(Lib.extendDeep({}, base, { - marker: { symbol: 'circle' } - })); - - expect(fullTrace.marker.color).toEqual(['red', 'green', 'blue']); - expect(fullTrace.marker.size).toEqual([10, 20, 30]); - }); - - it('should not fill *marker.line* in fullData while is not available', function() { - var fullTrace = _supply({ - mode: 'markers', - lon: [10, 20, 30], - lat: [10, 20, 30] - }); - - expect(fullTrace.marker).toBeDefined(); - expect(fullTrace.marker.line).toBeUndefined(); - }); -}); - -describe('scattermapbox convert', function() { - var base = { - type: 'scattermapbox', - lon: [10, '20', 30, 20, null, 20, 10], - lat: [20, 20, '10', null, 10, 10, 20] - }; - - function _convert(trace) { - var gd = { data: [trace] }; - supplyAllDefaults(gd); - - var fullTrace = gd._fullData[0]; - Plots.doCalcdata(gd, fullTrace); - - var calcTrace = gd.calcdata[0]; - - var mockAxis = {type: 'linear'}; - Axes.setConvert(mockAxis, gd._fullLayout); - - gd._fullLayout.mapbox._subplot = { - mockAxis: mockAxis - }; - - return convert(gd, calcTrace); - } - - function assertVisibility(opts, expectations) { - var actual = ['fill', 'line', 'circle', 'symbol'].map(function(l) { - return opts[l].layout.visibility; - }); - - expect(actual).toEqual(expectations, 'layer visibility'); - } - - it('should generate correct output for markers + circle bubbles traces', function() { - var opts = _convert(Lib.extendFlat({}, base, { - mode: 'markers', - marker: { - symbol: 'circle', - size: [10, 20, null, 10, '10'], - color: [10, null, '30', 20, 10] - } - })); - - assertVisibility(opts, ['none', 'none', 'visible', 'none']); - - expect(opts.circle.paint['circle-color']).toEqual({ - property: 'mcc', - type: 'identity' - }, 'circle-color paint'); - - expect(opts.circle.paint['circle-radius']).toEqual({ - property: 'mrc', - type: 'identity' - }, 'circle-radius paint'); - - expect(opts.circle.paint['circle-opacity']).toBe(0.7, 'circle-opacity'); - - var circleProps = opts.circle.geojson.features.map(function(f) { - return f.properties; - }); - - // N.B repeated values have same geojson props - expect(circleProps).toEqual([ - { mcc: 'rgb(220, 220, 220)', mrc: 5 }, - { mcc: '#444', mrc: 10 }, - { mcc: 'rgb(178, 10, 28)', mrc: 0 }, - { mcc: '#444', mrc: 0 }, - { mcc: '#444', mrc: 0 } - ], 'geojson feature properties'); - }); - - it('should fill circle-opacity correctly', function() { - var opts = _convert(Lib.extendFlat({}, base, { - mode: 'markers', - marker: { - symbol: 'circle', - size: 10, - color: 'red', - opacity: [1, null, 0.5, '0.5', '1', 0, 0.8] - }, - opacity: 0.5 - })); - - assertVisibility(opts, ['none', 'none', 'visible', 'none']); - expect(opts.circle.paint['circle-color']).toBe('red', 'circle-color'); - expect(opts.circle.paint['circle-radius']).toBe(5, 'circle-radius'); - - expect(opts.circle.paint['circle-opacity']).toEqual({ - property: 'mo', - type: 'identity' - }, 'circle-opacity paint'); - - var circleProps = opts.circle.geojson.features.map(function(f) { - return f.properties; - }); - - expect(circleProps).toEqual([ - { mo: 0.5 }, - { mo: 0 }, - { mo: 0.25 }, - // lat === null - // lon === null - { mo: 0 }, - { mo: 0.4 }, - ], 'geojson feature properties'); - }); - - it('should fill circle props correctly during selections', function() { - var _base = { - type: 'scattermapbox', - mode: 'markers', - lon: [-10, 30, 20], - lat: [45, 90, 180], - marker: {symbol: 'circle'} - }; - - var specs = [{ - msg: 'base case', - patch: { - selectedpoints: [1, 2] - }, - expected: { - opacity: [0.2, 1, 1] - } - }, { - msg: 'with set trace opacity', - patch: { - opacity: 0.5, - selectedpoints: [1, 2] - }, - expected: { - opacity: [0.1, 0.5, 0.5] - } - }, { - msg: 'with set scalar marker.opacity', - patch: { - marker: {opacity: 0.6}, - selectedpoints: [1, 2] - }, - expected: { - opacity: [0.12, 0.6, 0.6] - } - }, { - msg: 'width set array marker.opacity', - patch: { - marker: { - opacity: [0.5, 1, 0.6], - }, - selectedpoints: [0, 2] - }, - expected: { - opacity: [0.5, 0.2, 0.6] - } - }, { - msg: 'with set array marker.opacity including invalid items', - patch: { - marker: {opacity: [2, null, -0.6]}, - selectedpoints: [0, 1, 2] - }, - expected: { - opacity: [1, 0, 0] - } - }, { - msg: 'with set selected & unselected styles', - patch: { - selected: { - marker: { - opacity: 1, - color: 'green', - size: 20 - } - }, - unselected: { - marker: { - opacity: 0, - color: 'red', - size: 5 - } - }, - selectedpoints: [0, 2] - }, - expected: { - opacity: [1, 0, 1], - color: ['green', 'red', 'green'], - size: [10, 2.5, 10] - } - }, { - msg: 'with set selected styles only', - patch: { - selected: { - marker: { - opacity: 1, - color: 'green', - size: 20 - } - }, - selectedpoints: [0, 2] - }, - expected: { - opacity: [1, 0.2, 1], - color: ['green', '#1f77b4', 'green'], - size: [10, 3, 10] - } - }, { - msg: 'with set selected styles only + array items', - patch: { - marker: { - opacity: [0.5, 0.6, 0.7], - color: ['blue', 'yellow', 'cyan'], - size: [50, 60, 70] - }, - selected: { - marker: { - opacity: 1, - color: 'green', - size: 20 - } - }, - selectedpoints: [0, 2] - }, - expected: { - opacity: [1, 0.12, 1], - color: ['green', 'yellow', 'green'], - size: [10, 30, 10] - } - }, { - msg: 'with set unselected styles only', - patch: { - unselected: { - marker: { - opacity: 0, - color: 'red', - size: 5 - } - }, - selectedpoints: [0, 2] - }, - expected: { - opacity: [1, 0, 1], - color: ['#1f77b4', 'red', '#1f77b4'], - size: [3, 2.5, 3] - - } - }, { - msg: 'with set unselected styles only + array items', - patch: { - marker: { - opacity: [0.5, 0.6, 0.7], - color: ['blue', 'yellow', 'cyan'], - size: [50, 60, 70] - }, - unselected: { - marker: { - opacity: 0, - color: 'red', - size: 5 - } - }, - selectedpoints: [0, 2] - }, - expected: { - opacity: [0.5, 0, 0.7], - color: ['blue', 'red', 'cyan'], - size: [25, 2.5, 35] - } - }]; - - specs.forEach(function(s, i) { - var msg0 = s.msg + ' - case ' + i + '- '; - var opts = _convert(Lib.extendDeep({}, _base, s.patch)); - var features = opts.circle.geojson.features; - - function _assert(kProp, kExp) { - var actual = features.map(function(f) { return f.properties[kProp]; }); - var expected = s.expected[kExp]; - var msg = msg0 + ' marker.' + kExp; - - if(Array.isArray(expected)) { - expect(actual).toEqual(expected, msg); - } else { - actual.forEach(function(a) { - expect(a).toBe(undefined, msg); - }); - } - } - - _assert('mo', 'opacity'); - _assert('mcc', 'color'); - // N.B. sizes in props should be half of the input values - _assert('mrc', 'size'); - }); - }); - - it('should generate correct output for fill + markers + lines traces', function() { - var opts = _convert(Lib.extendFlat({}, base, { - mode: 'markers+lines', - marker: { symbol: 'circle' }, - fill: 'toself' - })); - - assertVisibility(opts, ['visible', 'visible', 'visible', 'none']); - - var segment1 = [[10, 20], [20, 20], [30, 10]]; - var segment2 = [[20, 10], [10, 20]]; - - var lineCoords = [segment1, segment2]; - var fillCoords = [[segment1], [segment2]]; - - expect(opts.line.geojson.coordinates).toEqual(lineCoords, 'line coords'); - expect(opts.fill.geojson.coordinates).toEqual(fillCoords, 'fill coords'); - - var circleCoords = opts.circle.geojson.features.map(function(f) { - return f.geometry.coordinates; - }); - - expect(circleCoords).toEqual([ - [10, 20], [20, 20], [30, 10], [20, 10], [10, 20] - ], 'circle coords'); - }); - - it('should generate correct output for markers + non-circle traces', function() { - var opts = _convert(Lib.extendFlat({}, base, { - mode: 'markers', - marker: { symbol: 'monument' } - })); - - assertVisibility(opts, ['none', 'none', 'none', 'visible']); - - var symbolProps = opts.symbol.geojson.features.map(function(f) { - return [f.properties.symbol, f.properties.text]; - }); - - var expected = opts.symbol.geojson.features.map(function() { - return ['monument', '']; - }); - - expect(symbolProps).toEqual(expected, 'geojson properties'); - }); - - - it('should allow symbols to be rotated and overlapped', function() { - var opts = _convert(Lib.extendFlat({}, base, { - mode: 'markers', - marker: { - symbol: ['monument', 'music', 'harbor'], - angle: [0, 90, 45], - allowoverlap: true - }, - })); - - var symbolAngle = opts.symbol.geojson.features.map(function(f) { - return f.properties.angle; - }); - - var expected = [0, 90, 45, 0, 0]; - expect(symbolAngle).toEqual(expected, 'geojson properties'); - - - expect(opts.symbol.layout['icon-rotate'].property).toEqual('angle', 'symbol.layout.icon-rotate'); - expect(opts.symbol.layout['icon-allow-overlap']).toEqual(true, 'symbol.layout.icon-allow-overlap'); - }); - - - it('should generate correct output for text + lines traces', function() { - var opts = _convert(Lib.extendFlat({}, base, { - mode: 'lines+text', - connectgaps: true, - text: ['A', 'B', 'C', 'D', 'E', 'F'] - })); - - assertVisibility(opts, ['none', 'visible', 'none', 'visible']); - - var lineCoords = [ - [10, 20], [20, 20], [30, 10], [20, 10], [10, 20] - ]; - - expect(opts.line.geojson.coordinates).toEqual(lineCoords, 'line coords'); - - var actualText = opts.symbol.geojson.features.map(function(f) { - return f.properties.text; - }); - - expect(actualText).toEqual(['A', 'B', 'C', 'F', undefined]); - }); - - it('should generate correct output for texttemplate without text', function() { - var opts = _convert(Lib.extendFlat({}, base, { - mode: 'lines+text', - connectgaps: true, - textposition: 'outside', - texttemplate: ['A', 'B', 'C', 'D', 'E', 'F'] - })); - - var actualText = opts.symbol.geojson.features.map(function(f) { - return f.properties.text; - }); - - expect(actualText).toEqual(['A', 'B', 'C', 'F', '']); - }); - - it('should convert \\n to \'\' and
to \\n', function() { - var opts = _convert(Lib.extendFlat({}, base, { - mode: 'text', - text: ['one\nline', 'two
lines', 'three
lines
yep'] - })); - - var actualText = opts.symbol.geojson.features.map(function(f) { - return f.properties.text; - }); - - expect(actualText).toEqual(['oneline', 'two\nlines', 'three\nlines\nyep', undefined, undefined]); - }); - - it('should convert \\n to \'\' and
to \\n - texttemplate case', function() { - var opts = _convert(Lib.extendFlat({}, base, { - mode: 'text', - texttemplate: ['%{lon}\none\nline', '%{lat}
two
lines', '%{lon}\n%{lat}
more
lines'] - })); - - var actualText = opts.symbol.geojson.features.map(function(f) { - return f.properties.text; - }); - - expect(actualText).toEqual(['10oneline', '20\ntwo\nlines', '3010\nmore\nlines', '', '']); - }); - - it('should generate correct output for texttemplate', function() { - var mock = { - type: 'scattermapbox', - mode: 'markers+text', - lon: [-73.57, -79.24, -123.06], - lat: [45.5, 43.4, 49.13], - text: ['Montreal', 'Toronto', 'Vancouver'], - texttemplate: '%{text} (%{lon}, %{lat}): %{customdata:.2s}', - textposition: 'top center', - customdata: [1780000, 2930000, 675218] - }; - var opts = _convert(mock); - var actualText = opts.symbol.geojson.features.map(function(f) { - return f.properties.text; - }); - - expect(actualText).toEqual([ - 'Montreal (−73.57, 45.5): 1.8M', - 'Toronto (−79.24, 43.4): 2.9M', - 'Vancouver (−123.06, 49.13): 680k' - ]); - }); - - it('should generate correct output for lines traces with trailing gaps', function() { - var opts = _convert(Lib.extendFlat({}, base, { - mode: 'lines', - lon: [10, '20', 30, 20, null, 20, 10, null, null], - lat: [20, 20, '10', null, 10, 10, 20, null] - })); - - assertVisibility(opts, ['none', 'visible', 'none', 'none']); - - var lineCoords = [ - [[10, 20], [20, 20], [30, 10]], - [[20, 10], [10, 20]] - ]; - - expect(opts.line.geojson.coordinates).toEqual(lineCoords, 'have correct line coords'); - }); - - it('should correctly convert \'textposition\' to \'text-anchor\' and \'text-offset\'', function() { - var specs = { - 'top left': ['top-right', [-0.65, -1.65]], - 'top center': ['top', [0, -1.65]], - 'top right': ['top-left', [0.65, -1.65]], - 'middle left': ['right', [-0.65, 0]], - 'middle center': ['center', [0, 0]], - 'middle right': ['left', [0.65, 0]], - 'bottom left': ['bottom-right', [-0.65, 1.65]], - 'bottom center': ['bottom', [0, 1.65]], - 'bottom right': ['bottom-left', [0.65, 1.65]] - }; - - Object.keys(specs).forEach(function(k) { - var spec = specs[k]; - - var opts = _convert(Lib.extendFlat({}, base, { - textposition: k, - mode: 'text+markers', - marker: { size: 15 }, - text: ['A', 'B', 'C'] - })); - - expect([ - opts.symbol.layout['text-anchor'], - opts.symbol.layout['text-offset'] - ]).toEqual(spec, '(case ' + k + ')'); - }); - }); - - it('should generate correct output for markers + circle bubbles traces with repeated values', function() { - var opts = _convert(Lib.extendFlat({}, base, { - lon: ['-96.796988', '-81.379236', '-85.311819', ''], - lat: ['32.776664', '28.538335', '35.047157', '' ], - marker: { size: ['5', '49', '5', ''] } - })); - - expect(opts.circle.paint['circle-radius']).toEqual({ - property: 'mrc', - type: 'identity' - }, 'circle-radius paint'); - - var radii = opts.circle.geojson.features.map(function(f) { - return f.properties.mrc; - }); - - expect(radii).toBeCloseToArray([2.5, 24.5, 2.5], 'circle radii'); - }); - - it('should generate correct output for traces with only blank points', function() { - var opts = _convert(Lib.extendFlat({}, base, { - mode: 'lines', - lon: ['', null], - lat: [null, ''], - fill: 'toself' - })); - - // not optimal, but doesn't break anything as mapbox-gl accepts empty - // coordinate arrays - assertVisibility(opts, ['visible', 'visible', 'none', 'none']); - - expect(opts.line.geojson.coordinates).toEqual([], 'line coords'); - expect(opts.fill.geojson.coordinates).toEqual([], 'fill coords'); - }); - - it('cluster options', function() { - var opts = _convert(Lib.extendFlat({}, base, { - cluster: { - enabled: true - } - })); - - // Ensure that cluster and clusterCount options is added to options - expect(opts.cluster).toBeInstanceOf(Object); - expect(opts.clusterCount).toBeInstanceOf(Object); - - // Ensure correct type of layers - expect(opts.cluster.type).toEqual('circle'); - expect(opts.clusterCount.type).toEqual('symbol'); - }); - - it('cluster colors, sizes, opacities - array', function() { - var opts = _convert(Lib.extendFlat({}, base, { - cluster: { - enabled: true, - color: 'red', - size: 20, - opacity: 0.25 - } - })); - - expect(opts.cluster.paint['circle-color']).toEqual('red'); - expect(opts.cluster.paint['circle-radius']).toEqual(20); - expect(opts.cluster.paint['circle-opacity']).toEqual(0.25); - }); - - it('cluster colors, sizes, opacities - array', function() { - var opts = _convert(Lib.extendFlat({}, base, { - cluster: { - enabled: true, - step: [10], - color: ['red', 'green'], - size: [20, 40], - opacity: [0.25, 0.75] - } - })); - - expect(opts.cluster.paint['circle-color']).toEqual(['step', ['get', 'point_count'], 'red', 10, 'green']); - expect(opts.cluster.paint['circle-radius']).toEqual(['step', ['get', 'point_count'], 20, 10, 40]); - expect(opts.cluster.paint['circle-opacity']).toEqual(['step', ['get', 'point_count'], 0.25, 10, 0.75]); - }); -}); - -describe('scattermapbox hover', function() { - var hoverPoints = ScatterMapbox.hoverPoints; - var gd; - - beforeAll(function(done) { - Plotly.setPlotConfig({ - mapboxAccessToken: require('../../../build/credentials.json').MAPBOX_ACCESS_TOKEN - }); - - gd = createGraphDiv(); - - var data = [{ - type: 'scattermapbox', - lon: [10, 20, 30, 300], - lat: [10, 20, 30, 10], - text: ['A', 'B', 'C', 'D'] - }]; - - Plotly.newPlot(gd, data, { autosize: true }).then(done); - }); - - afterAll(function() { - Plotly.purge(gd); - destroyGraphDiv(); - }); - - function getPointData(gd) { - var cd = gd.calcdata; - var subplot = gd._fullLayout.mapbox._subplot; - - return { - index: false, - distance: 20, - cd: cd[0], - trace: cd[0][0].trace, - subplot: subplot, - xa: subplot.xaxis, - ya: subplot.yaxis - }; - } - - function checkHoverLabel(pos, content) { - mouseEvent('mousemove', pos[0], pos[1]); - - assertHoverLabelContent({ - nums: content[0], - name: content[1] - }); - } - - it('@gl should generate hover label info (base case)', function() { - var xval = 11; - var yval = 11; - - var out = hoverPoints(getPointData(gd), xval, yval)[0]; - - expect(out.index).toEqual(0); - expect([out.x0, out.x1, out.y0, out.y1]).toBeCloseToArray([ - 297.444, 299.444, 105.410, 107.410 - ]); - expect(out.extraText).toEqual('(10°, 10°)
A'); - expect(out.color).toEqual('#1f77b4'); - }); - - it('@gl should generate hover label info (lon > 180 case)', function() { - var xval = 301; - var yval = 11; - var out = hoverPoints(getPointData(gd), xval, yval)[0]; - - expect(out.index).toEqual(3); - expect([out.x0, out.x1, out.y0, out.y1]).toBeCloseToArray([ - 1122.33, 1124.33, 105.41, 107.41 - ]); - expect(out.extraText).toEqual('(10°, 300°)
D'); - expect(out.color).toEqual('#1f77b4'); - }); - - it('@gl should skip over blank and non-string text items', function(done) { - var xval = 11; - var yval = 11; - var out; - - Plotly.restyle(gd, 'text', [['', 'B', 'C']]).then(function() { - out = hoverPoints(getPointData(gd), xval, yval)[0]; - expect(out.extraText).toEqual('(10°, 10°)'); - - return Plotly.restyle(gd, 'text', [[null, 'B', 'C']]); - }) - .then(function() { - out = hoverPoints(getPointData(gd), xval, yval)[0]; - expect(out.extraText).toEqual('(10°, 10°)'); - - return Plotly.restyle(gd, 'text', [[false, 'B', 'C']]); - }) - .then(function() { - out = hoverPoints(getPointData(gd), xval, yval)[0]; - expect(out.extraText).toEqual('(10°, 10°)'); - - return Plotly.restyle(gd, 'text', [['A', 'B', 'C']]); - }) - .then(function() { - out = hoverPoints(getPointData(gd), xval, yval)[0]; - expect(out.extraText).toEqual('(10°, 10°)
A'); - }) - .then(done, done.fail); - }); - - it('@gl should generate hover label info (positive winding case)', function() { - var xval = 11 + 720; - var yval = 11; - - var out = hoverPoints(getPointData(gd), xval, yval)[0]; - - expect(out.index).toEqual(0); - expect([out.x0, out.x1, out.y0, out.y1]).toBeCloseToArray([ - 2345.444, 2347.444, 105.410, 107.410 - ]); - expect(out.extraText).toEqual('(10°, 10°)
A'); - expect(out.color).toEqual('#1f77b4'); - }); - - it('@gl should generate hover label info (negative winding case)', function() { - var xval = 11 - 1080; - var yval = 11; - - var out = hoverPoints(getPointData(gd), xval, yval)[0]; - - expect(out.index).toEqual(0); - expect([out.x0, out.x1, out.y0, out.y1]).toBeCloseToArray([ - -2774.555, -2772.555, 105.410, 107.410 - ]); - expect(out.extraText).toEqual('(10°, 10°)
A'); - expect(out.color).toEqual('#1f77b4'); - }); - - it('@gl should generate hover label info (hoverinfo: \'lon\' case)', function(done) { - Plotly.restyle(gd, 'hoverinfo', 'lon').then(function() { - var xval = 11; - var yval = 11; - - var out = hoverPoints(getPointData(gd), xval, yval)[0]; - - expect(out.extraText).toEqual('lon: 10°'); - }) - .then(done, done.fail); - }); - - it('@gl should generate hover label info (hoverinfo: \'lat\' case)', function(done) { - Plotly.restyle(gd, 'hoverinfo', 'lat').then(function() { - var xval = 11; - var yval = 11; - - var out = hoverPoints(getPointData(gd), xval, yval)[0]; - - expect(out.extraText).toEqual('lat: 10°'); - }) - .then(done, done.fail); - }); - - it('@gl should generate hover label info (hoverinfo: \'text\' + \'text\' array case)', function(done) { - Plotly.restyle(gd, 'hoverinfo', 'text').then(function() { - var xval = 11; - var yval = 11; - - var out = hoverPoints(getPointData(gd), xval, yval)[0]; - - expect(out.extraText).toEqual('A'); - }) - .then(done, done.fail); - }); - - it('@gl should generate hover label info (hoverinfo: \'text\' + \'hovertext\' array case)', function(done) { - Plotly.restyle(gd, 'hovertext', ['Apple', 'Banana', 'Orange']).then(function() { - var xval = 11; - var yval = 11; - - var out = hoverPoints(getPointData(gd), xval, yval)[0]; - - expect(out.extraText).toEqual('Apple'); - }) - .then(done, done.fail); - }); - - it('@gl should generate hover label (\'marker.color\' array case)', function(done) { - Plotly.restyle(gd, 'marker.color', [['red', 'blue', 'green']]).then(function() { - var out = hoverPoints(getPointData(gd), 11, 11)[0]; - - expect(out.color).toEqual('red'); - }) - .then(done, done.fail); - }); - - it('@gl should generate hover label (\'marker.color\' w/ colorscale case)', function(done) { - Plotly.restyle(gd, 'marker.color', [[10, 5, 30]]).then(function() { - var out = hoverPoints(getPointData(gd), 11, 11)[0]; - - expect(out.color).toEqual('rgb(245, 195, 157)'); - }) - .then(done, done.fail); - }); - - it('@gl should generate hover label (\'hoverinfo\' array case)', function(done) { - function check(expected) { - var out = hoverPoints(getPointData(gd), 11, 11)[0]; - expect(out.extraText).toEqual(expected); - } - - Plotly.restyle(gd, 'hoverinfo', [['lon', 'lat', 'lon+lat+name']]).then(function() { - check('lon: 10°'); - return Plotly.restyle(gd, 'hoverinfo', [['lat', 'lon', 'name']]); - }) - .then(function() { - check('lat: 10°'); - return Plotly.restyle(gd, 'hoverinfo', [['text', 'lon', 'name']]); - }) - .then(function() { - check('Apple'); - return Plotly.restyle(gd, 'hoverinfo', [[null, 'lon', 'name']]); - }) - .then(function() { - check('(10°, 10°)
Apple'); - }) - .then(done, done.fail); - }); - - it('@gl should pass along hovertemplate', function(done) { - Plotly.restyle(gd, 'hovertemplate', 'tpl').then(function() { - var xval = 11; - var yval = 11; - - var out = hoverPoints(getPointData(gd), xval, yval)[0]; - - expect(out.hovertemplate).toEqual('tpl'); - }) - .then(done, done.fail); - }); - - it('@gl should always display hoverlabel when hovertemplate is defined', function(done) { - Plotly.restyle(gd, { - name: '', - hovertemplate: 'tpl2' - }) - .then(function() { - checkHoverLabel([190, 215], ['tpl2', '']); - }) - .then(done, done.fail); - }); -}); - -describe('Test plotly events on a scattermapbox plot:', function() { - var mock = require('../../image/mocks/mapbox_0.json'); - var pointPos = [440, 290]; - var nearPos = [460, 290]; - var blankPos = [10, 10]; - var mockCopy; - var gd; - - beforeAll(function() { - Plotly.setPlotConfig({ - mapboxAccessToken: require('../../../build/credentials.json').MAPBOX_ACCESS_TOKEN - }); - }); - - beforeEach(function(done) { - gd = createGraphDiv(); - mockCopy = Lib.extendDeep({}, mock); - mockCopy.layout.width = 800; - mockCopy.layout.height = 500; - Plotly.newPlot(gd, mockCopy).then(done); - }); - - afterEach(destroyGraphDiv); - - describe('click events', function() { - var futureData; - - beforeEach(function() { - futureData = null; - - gd.on('plotly_click', function(data) { - futureData = data; - }); - }); - - it('@gl should not be trigged when not on data points', function() { - click(blankPos[0], blankPos[1]); - expect(futureData).toBe(null); - }); - - it('@gl should contain the correct fields', function() { - click(pointPos[0], pointPos[1]); - - var pt = futureData.points[0]; - - expect(Object.keys(pt).sort()).toEqual([ - 'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex', 'lon', 'lat', 'bbox' - ].sort()); - - expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); - expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); - expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); - expect(pt.lat).toEqual(10, 'points[0].lat'); - expect(pt.lon).toEqual(10, 'points[0].lon'); - expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); - }); - }); - - describe('modified click events', function() { - var clickOpts = { - altKey: true, - ctrlKey: true, - metaKey: true, - shiftKey: true - }; - - var futureData; - - beforeEach(function() { - futureData = null; - - gd.on('plotly_click', function(data) { - futureData = data; - }); - }); - - it('@gl should not be trigged when not on data points', function() { - click(blankPos[0], blankPos[1], clickOpts); - expect(futureData).toBe(null); - }); - - it('@gl does not register right-clicks', function() { - click(pointPos[0], pointPos[1], clickOpts); - expect(futureData).toBe(null); - - // TODO: 'should contain the correct fields' - // This test passed previously, but only because assets/click - // incorrectly generated a click event for right click. It never - // worked in reality. - // var pt = futureData.points[0], - // evt = futureData.event; - - // expect(Object.keys(pt).sort()).toEqual([ - // 'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex', 'lon', 'lat', 'bbox' - // ].sort()); - - // expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); - // expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); - // expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); - // expect(pt.lat).toEqual(10, 'points[0].lat'); - // expect(pt.lon).toEqual(10, 'points[0].lon'); - // expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); - - // Object.getOwnPropertyNames(clickOpts).forEach(function(opt) { - // expect(evt[opt]).toEqual(clickOpts[opt], 'event.' + opt); - // }); - }); - }); - - describe('hover events', function() { - var futureData; - - beforeEach(function() { - futureData = null; - - gd.on('plotly_hover', function(data) { - futureData = data; - }); - }); - - it('@gl should contain the correct fields', function() { - mouseEvent('mousemove', blankPos[0], blankPos[1]); - mouseEvent('mousemove', pointPos[0], pointPos[1]); - - var pt = futureData.points[0]; - - expect(Object.keys(pt).sort()).toEqual([ - 'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex', 'lon', 'lat', 'bbox' - ].sort()); - - expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); - expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); - expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); - expect(pt.lat).toEqual(10, 'points[0].lat'); - expect(pt.lon).toEqual(10, 'points[0].lon'); - expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); - }); - }); - - describe('unhover events', function() { - var futureData; - - beforeEach(function() { - futureData = null; - - gd.on('plotly_unhover', function(data) { - futureData = data; - }); - }); - - it('@gl should contain the correct fields', function(done) { - move(pointPos[0], pointPos[1], nearPos[0], nearPos[1], HOVERMINTIME + 10).then(function() { - var pt = futureData.points[0]; - - expect(Object.keys(pt).sort()).toEqual([ - 'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex', 'lon', 'lat', 'bbox' - ].sort()); - - expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); - expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); - expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); - expect(pt.lat).toEqual(10, 'points[0].lat'); - expect(pt.lon).toEqual(10, 'points[0].lon'); - expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); - }) - .then(done, done.fail); - }); - }); -}); - -describe('Test plotly events on a scattermapbox plot when css transform is present:', function() { - var mock = require('../../image/mocks/mapbox_0.json'); - var pointPos = [440 / 2, 290 / 2]; - var nearPos = [460 / 2, 290 / 2]; - var blankPos = [10 / 2, 10 / 2]; - var mockCopy; - var gd; - - function transformPlot(gd, transformString) { - gd.style.webkitTransform = transformString; - gd.style.MozTransform = transformString; - gd.style.msTransform = transformString; - gd.style.OTransform = transformString; - gd.style.transform = transformString; - } - - beforeAll(function() { - Plotly.setPlotConfig({ - mapboxAccessToken: require('../../../build/credentials.json').MAPBOX_ACCESS_TOKEN - }); - }); - - beforeEach(function(done) { - gd = createGraphDiv(); - mockCopy = Lib.extendDeep({}, mock); - mockCopy.layout.width = 800; - mockCopy.layout.height = 500; - - Plotly.newPlot(gd, mockCopy) - .then(function() { transformPlot(gd, 'translate(-25%, -25%) scale(0.5)'); }) - .then(done); - }); - - afterEach(destroyGraphDiv); - - describe('click events', function() { - var futureData; - - beforeEach(function() { - futureData = null; - - gd.on('plotly_click', function(data) { - futureData = data; - }); - }); - - it('@gl should not be trigged when not on data points', function() { - click(blankPos[0], blankPos[1]); - expect(futureData).toBe(null); - }); - - it('@gl should contain the correct fields', function() { - click(pointPos[0], pointPos[1]); - - var pt = futureData.points[0]; - - expect(Object.keys(pt).sort()).toEqual([ - 'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex', 'lon', 'lat', 'bbox' - ].sort()); - - expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); - expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); - expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); - expect(pt.lat).toEqual(10, 'points[0].lat'); - expect(pt.lon).toEqual(10, 'points[0].lon'); - expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); - }); - }); - - describe('hover events', function() { - var futureData; - - beforeEach(function() { - futureData = null; - - gd.on('plotly_hover', function(data) { - futureData = data; - }); - }); - - it('@gl should contain the correct fields', function() { - mouseEvent('mousemove', blankPos[0], blankPos[1]); - mouseEvent('mousemove', pointPos[0], pointPos[1]); - - var pt = futureData.points[0]; - - expect(Object.keys(pt).sort()).toEqual([ - 'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex', 'lon', 'lat', 'bbox' - ].sort()); - - expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); - expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); - expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); - expect(pt.lat).toEqual(10, 'points[0].lat'); - expect(pt.lon).toEqual(10, 'points[0].lon'); - expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); - }); - }); - - describe('unhover events', function() { - var futureData; - - beforeEach(function() { - futureData = null; - - gd.on('plotly_unhover', function(data) { - futureData = data; - }); - }); - - it('@gl should contain the correct fields', function(done) { - move(pointPos[0], pointPos[1], nearPos[0], nearPos[1], HOVERMINTIME + 10).then(function() { - var pt = futureData.points[0]; - - expect(Object.keys(pt).sort()).toEqual([ - 'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex', 'lon', 'lat', 'bbox' - ].sort()); - - expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); - expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); - expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); - expect(pt.lat).toEqual(10, 'points[0].lat'); - expect(pt.lon).toEqual(10, 'points[0].lon'); - expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); - }) - .then(done, done.fail); - }); - }); -}); - -describe('scattermapbox restyle', function() { - var gd; - - beforeAll(function() { - Plotly.setPlotConfig({ - mapboxAccessToken: require('../../../build/credentials.json').MAPBOX_ACCESS_TOKEN - }); - - gd = createGraphDiv(); - }); - - afterAll(function() { - Plotly.purge(gd); - destroyGraphDiv(); - }); - - it('@gl should be able to update legendonly to visible', function(done) { - Plotly.newPlot(gd, { - data: [{ - lat: [0, 2], lon: [0, 2], - type: 'scattermapbox', - mode: 'lines', - visible: 'legendonly' - }, - { - lat: [0, 2], lon: [2, 0], - type: 'scattermapbox', - mode: 'lines', - visible: true - } - ], layout: { - mapbox: { - style: 'open-street-map', - zoom: 6, - center: { lat: 1, lon: 1 } - }, - showlegend: true - } - }).then(function() { - return Plotly.restyle(gd, 'visible', true); - }).then(done, done.fail); - }); -}); diff --git a/test/jasmine/tests/select_test.js b/test/jasmine/tests/select_test.js index 3d345c63a3a..251737d9165 100644 --- a/test/jasmine/tests/select_test.js +++ b/test/jasmine/tests/select_test.js @@ -691,10 +691,8 @@ describe('Click-to-select', function() { }); [ - testCase('scattermapbox', require('../../image/mocks/mapbox_0.json'), 650, 195, [[2], []], {}, - { mapboxAccessToken: require('../../../build/credentials.json').MAPBOX_ACCESS_TOKEN }), - testCase('choroplethmapbox', require('../../image/mocks/mapbox_choropleth0.json'), 270, 220, [[0]], {}, - { mapboxAccessToken: require('../../../build/credentials.json').MAPBOX_ACCESS_TOKEN }) + testCase('scattermap', require('../../image/mocks/map_0.json'), 650, 195, [[2], []], {}, {}), + testCase('choroplethmap', require('../../image/mocks/map_choropleth0.json'), 270, 220, [[0]], {}, {}) ] .forEach(function(testCase) { it('@gl trace type ' + testCase.label, function(done) { @@ -788,10 +786,8 @@ describe('Click-to-select', function() { }); [ - testCase('mapbox', require('../../image/mocks/mapbox_0.json'), 650, 195, [[2], []], {}, - { mapboxAccessToken: require('../../../build/credentials.json').MAPBOX_ACCESS_TOKEN }), - testCase('mapbox', require('../../image/mocks/mapbox_choropleth0.json'), 270, 220, [[0], []], {}, - { mapboxAccessToken: require('../../../build/credentials.json').MAPBOX_ACCESS_TOKEN }) + testCase('scattermap', require('../../image/mocks/map_0.json'), 650, 195, [[2], []], {}, {}), + testCase('choroplethmap', require('../../image/mocks/map_choropleth0.json'), 270, 220, [[0], []], {}, {}) ].forEach(function(testCase) { it('@gl for base plot ' + testCase.label, function(done) { _run(testCase, done); @@ -2158,70 +2154,6 @@ describe('Test select box and lasso per trace:', function() { }); }); - [false, true].forEach(function(hasCssTransform) { - it('@gl should work on scattermapbox traces, hasCssTransform: ' + hasCssTransform, function(done) { - var assertPoints = makeAssertPoints(['lon', 'lat']); - var assertRanges = makeAssertRanges('mapbox'); - var assertLassoPoints = makeAssertLassoPoints('mapbox'); - var assertSelectedPoints = makeAssertSelectedPoints(); - - var fig = Lib.extendDeep({}, require('../../image/mocks/mapbox_bubbles-text')); - - fig.data[0].lon.push(null); - fig.data[0].lat.push(null); - - fig.layout.dragmode = 'select'; - - delete fig.layout.mapbox.bounds; - - fig.config = { - mapboxAccessToken: require('../../../build/credentials.json').MAPBOX_ACCESS_TOKEN - }; - addInvisible(fig); - - _newPlot(gd, fig) - .then(function() { - if(hasCssTransform) transformPlot(gd, cssTransform); - - return _run(hasCssTransform, - [[370, 120], [500, 200]], - function() { - assertPoints([[30, 30]]); - assertRanges([[21.99, 34.55], [38.14, 25.98]]); - assertSelectedPoints({0: [2]}); - }, - null, BOXEVENTS, 'scattermapbox select' - ); - }) - .then(function() { - return Plotly.relayout(gd, 'dragmode', 'lasso'); - }) - .then(function() { - return _run(hasCssTransform, - [[300, 200], [300, 300], [400, 300], [400, 200], [300, 200]], - function() { - assertPoints([[20, 20]]); - assertSelectedPoints({0: [1]}); - assertLassoPoints([ - [13.28, 25.97], [13.28, 14.33], [25.71, 14.33], [25.71, 25.97], [13.28, 25.97] - ]); - }, - null, LASSOEVENTS, 'scattermapbox lasso' - ); - }) - .then(function() { - // make selection handlers don't get called in 'pan' dragmode - return Plotly.relayout(gd, 'dragmode', 'pan'); - }) - .then(function() { - return _run(hasCssTransform, - [[370, 120], [500, 200]], null, null, NOEVENTS, 'scattermapbox pan' - ); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - }); - [false, true].forEach(function(hasCssTransform) { it('@gl should work on scattermap traces, hasCssTransform: ' + hasCssTransform, function(done) { var assertPoints = makeAssertPoints(['lon', 'lat']); @@ -2346,58 +2278,6 @@ describe('Test select box and lasso per trace:', function() { }, LONG_TIMEOUT_INTERVAL); }); - [false, true].forEach(function(hasCssTransform) { - it('@gl should work on choroplethmapbox traces, hasCssTransform: ' + hasCssTransform, function(done) { - var assertPoints = makeAssertPoints(['location', 'z']); - var assertRanges = makeAssertRanges('mapbox'); - var assertLassoPoints = makeAssertLassoPoints('mapbox'); - var assertSelectedPoints = makeAssertSelectedPoints(); - - var fig = Lib.extendDeep({}, require('../../image/mocks/mapbox_choropleth0.json')); - - fig.data[0].locations.push(null); - - fig.layout.dragmode = 'select'; - fig.config = { - mapboxAccessToken: require('../../../build/credentials.json').MAPBOX_ACCESS_TOKEN - }; - addInvisible(fig); - - _newPlot(gd, fig) - .then(function() { - if(hasCssTransform) transformPlot(gd, cssTransform); - - return _run(hasCssTransform, - [[150, 150], [300, 300]], - function() { - assertPoints([['NY', 10]]); - assertRanges([[-83.38, 46.13], [-74.06, 39.29]]); - assertSelectedPoints({0: [0]}); - }, - null, BOXEVENTS, 'choroplethmapbox select' - ); - }) - .then(function() { - return Plotly.relayout(gd, 'dragmode', 'lasso'); - }) - .then(function() { - return _run(hasCssTransform, - [[300, 200], [300, 300], [400, 300], [400, 200], [300, 200]], - function() { - assertPoints([['MA', 20]]); - assertSelectedPoints({0: [1]}); - assertLassoPoints([ - [-74.06, 43.936], [-74.06, 39.293], [-67.84, 39.293], - [-67.84, 43.936], [-74.06, 43.936] - ]); - }, - null, LASSOEVENTS, 'choroplethmapbox lasso' - ); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - }); - [false, true].forEach(function(hasCssTransform) { it('@gl should work on choroplethmap traces, hasCssTransform: ' + hasCssTransform, function(done) { var assertPoints = makeAssertPoints(['location', 'z']); @@ -3468,67 +3348,6 @@ describe('Test select box and lasso per trace:', function() { }); }); - it('@gl should work on choroplethmapbox traces after adding a new trace on top:', function(done) { - var assertPoints = makeAssertPoints(['location', 'z']); - var assertRanges = makeAssertRanges('mapbox'); - var assertLassoPoints = makeAssertLassoPoints('mapbox'); - var assertSelectedPoints = makeAssertSelectedPoints(); - - var fig = Lib.extendDeep({}, require('../../image/mocks/mapbox_choropleth0.json')); - - fig.data[0].locations.push(null); - - fig.layout.dragmode = 'select'; - fig.config = { - mapboxAccessToken: require('../../../build/credentials.json').MAPBOX_ACCESS_TOKEN - }; - addInvisible(fig); - - var hasCssTransform = false; - - _newPlot(gd, fig) - .then(function() { - // add a scatter points on top - fig.data[3] = { - type: 'scattermapbox', - marker: { size: 40 }, - lon: [-70], - lat: [40] - }; - - return Plotly.react(gd, fig); - }) - .then(function() { - return _run(hasCssTransform, - [[150, 150], [300, 300]], - function() { - assertPoints([['NY', 10]]); - assertRanges([[-83.38, 46.13], [-74.06, 39.29]]); - assertSelectedPoints({0: [0], 3: []}); - }, - null, BOXEVENTS, 'choroplethmapbox select' - ); - }) - .then(function() { - return Plotly.relayout(gd, 'dragmode', 'lasso'); - }) - .then(function() { - return _run(hasCssTransform, - [[300, 200], [300, 300], [400, 300], [400, 200], [300, 200]], - function() { - assertPoints([['MA', 20], []]); - assertSelectedPoints({0: [1], 3: [0]}); - assertLassoPoints([ - [-74.06, 43.936], [-74.06, 39.293], [-67.84, 39.293], - [-67.84, 43.936], [-74.06, 43.936] - ]); - }, - null, LASSOEVENTS, 'choroplethmapbox lasso' - ); - }) - .then(done, done.fail); - }, LONG_TIMEOUT_INTERVAL); - it('@gl should work on choroplethmap traces after adding a new trace on top:', function(done) { var assertPoints = makeAssertPoints(['location', 'z']); var assertRanges = makeAssertRanges('map');