From 34158c1e878ebb3056fdd42ebc021e183b3447ce Mon Sep 17 00:00:00 2001 From: archmoj Date: Fri, 22 Mar 2019 18:03:15 -0400 Subject: [PATCH 1/7] using different retry numbers for gl and flaky containers reduced number of retries for jasmine-2 reduced number of retries for jasmine-3 adjusted timeouts to help fix test errors on CI removed noCI tags from a number of tests set shards limit to 1 - fixed various side effects between different tests reduce n it limit to reduce side effects between different tests now we can reduce retries for both now test if we could remove some noCI flags from the tests reset few noCI flags final considerations --- .circleci/test.sh | 10 +++---- tasks/shard_jasmine_tests.js | 2 +- test/jasmine/tests/gl2d_click_test.js | 4 +-- test/jasmine/tests/gl2d_plot_interact_test.js | 6 ++-- test/jasmine/tests/gl2d_pointcloud_test.js | 12 ++++---- test/jasmine/tests/parcoords_test.js | 12 ++++---- test/jasmine/tests/plot_api_react_test.js | 6 ++-- test/jasmine/tests/polar_test.js | 29 +++++++++++++------ test/jasmine/tests/select_test.js | 2 +- test/jasmine/tests/splom_test.js | 4 +-- 10 files changed, 49 insertions(+), 38 deletions(-) diff --git a/.circleci/test.sh b/.circleci/test.sh index 62fd478837f..4dbabf8879f 100755 --- a/.circleci/test.sh +++ b/.circleci/test.sh @@ -6,13 +6,13 @@ set +o pipefail ROOT=$(dirname $0)/.. EXIT_STATE=0 -MAX_AUTO_RETRY=5 log () { echo -e "\n$1" } # inspired by https://unix.stackexchange.com/a/82602 +MAX_AUTO_RETRY=1 retry () { local n=1 @@ -54,9 +54,9 @@ case $1 in set_tz SHARDS=($(node $ROOT/tasks/shard_jasmine_tests.js --tag=gl)) - + MAX_AUTO_RETRY=2 for s in ${SHARDS[@]}; do - retry npm run test-jasmine -- "$s" --tags=gl --skip-tags=noCI + retry npm run test-jasmine -- "${s}" --tags=gl --skip-tags=noCI done exit $EXIT_STATE @@ -66,9 +66,9 @@ case $1 in set_tz SHARDS=($(node $ROOT/tasks/shard_jasmine_tests.js --tag=flaky)) - + MAX_AUTO_RETRY=5 for s in ${SHARDS[@]}; do - retry npm run test-jasmine -- "$s" --tags=flaky --skip-tags=noCI + retry npm run test-jasmine -- "${s}" --tags=flaky --skip-tags=noCI done exit $EXIT_STATE diff --git a/tasks/shard_jasmine_tests.js b/tasks/shard_jasmine_tests.js index 9b5fbdf04ba..88d600cb883 100644 --- a/tasks/shard_jasmine_tests.js +++ b/tasks/shard_jasmine_tests.js @@ -15,7 +15,7 @@ var argv = minimist(process.argv.slice(2), { limit: ['l'], }, default: { - limit: 20 + limit: 1 } }); diff --git a/test/jasmine/tests/gl2d_click_test.js b/test/jasmine/tests/gl2d_click_test.js index 0d567899011..6167a7bc332 100644 --- a/test/jasmine/tests/gl2d_click_test.js +++ b/test/jasmine/tests/gl2d_click_test.js @@ -561,7 +561,7 @@ describe('Test hover and click interactions', function() { }); }); -describe('@noCI Test gl2d lasso/select:', function() { +describe('Test gl2d lasso/select:', function() { var mockFancy = require('@mocks/gl2d_14.json'); delete mockFancy.layout.xaxis.autorange; delete mockFancy.layout.yaxis.autorange; @@ -613,7 +613,7 @@ describe('@noCI Test gl2d lasso/select:', function() { function select(path) { return new Promise(function(resolve, reject) { gd.once('plotly_selected', resolve); - setTimeout(function() { reject('did not trigger *plotly_selected*');}, 200); + setTimeout(function() { reject('did not trigger *plotly_selected*');}, 300); drag(path); }); } diff --git a/test/jasmine/tests/gl2d_plot_interact_test.js b/test/jasmine/tests/gl2d_plot_interact_test.js index 88017a95a75..0db0f1d3586 100644 --- a/test/jasmine/tests/gl2d_plot_interact_test.js +++ b/test/jasmine/tests/gl2d_plot_interact_test.js @@ -215,7 +215,7 @@ describe('Test gl plot side effects', function() { .then(done); }); - it('@noCI @gl should fire *plotly_webglcontextlost* when on webgl context lost', function(done) { + it('@gl should fire *plotly_webglcontextlost* when on webgl context lost', function(done) { var _mock = Lib.extendDeep({}, require('@mocks/gl2d_12.json')); function _trigger(name) { @@ -551,7 +551,7 @@ describe('Test gl2d plots', function() { .then(done); }); - it('@noCI @gl should display selection of big number of regular points', function(done) { + it('@gl should display selection of big number of regular points', function(done) { // generate large number of points var x = []; var y = []; @@ -582,7 +582,7 @@ describe('Test gl2d plots', function() { .then(done); }); - it('@noCI @gl should display selection of big number of miscellaneous points', function(done) { + it('@gl should display selection of big number of miscellaneous points', function(done) { var colorList = [ '#006385', '#F06E75', '#90ed7d', '#f7a35c', '#8085e9', '#f15c80', '#e4d354', '#2b908f', '#f45b5b', '#91e8e1', diff --git a/test/jasmine/tests/gl2d_pointcloud_test.js b/test/jasmine/tests/gl2d_pointcloud_test.js index 9ef51ffba4e..6ff76e1c520 100644 --- a/test/jasmine/tests/gl2d_pointcloud_test.js +++ b/test/jasmine/tests/gl2d_pointcloud_test.js @@ -196,7 +196,7 @@ describe('pointcloud traces', function() { it('@gl should not change other traces colors', function(done) { var _mock = Lib.extendDeep({}, multipleScatter2dMock); Plotly.plot(gd, _mock) - .then(delay(40)) + .then(delay(20)) .then(function() { var canvas = d3.select('.gl-canvas-context').node(); @@ -226,13 +226,13 @@ describe('pointcloud traces', function() { } Plotly.plot(gd, Lib.extendDeep({}, plotData)) - .then(delay(40)) + .then(delay(20)) .then(function() { _assertRange('base', [-0.548, 9.548], [-1.415, 10.415]); }) - .then(delay(40)) + .then(delay(20)) .then(function() { _drag([200, 200], [350, 350]); }) - .then(delay(40)) + .then(delay(20)) .then(function() { _assertRange('after zoombox drag', [0.768, 1.591], [5.462, 7.584]); }) @@ -248,9 +248,9 @@ describe('pointcloud traces', function() { .then(function() { return Plotly.relayout(gd, 'dragmode', 'pan'); }) - .then(delay(40)) + .then(delay(20)) .then(function() { _drag([200, 200], [350, 350]); }) - .then(delay(40)) + .then(delay(20)) .then(function() { _assertRange('after pan drag', [0.2743, 10.3719], [-3.537, 8.292]); }) diff --git a/test/jasmine/tests/parcoords_test.js b/test/jasmine/tests/parcoords_test.js index 863fcd1a531..1c4b7d027da 100644 --- a/test/jasmine/tests/parcoords_test.js +++ b/test/jasmine/tests/parcoords_test.js @@ -509,7 +509,7 @@ describe('parcoords edge cases', function() { .then(done); }); - it('@noCI @gl Works with 60 dimensions', function(done) { + it('@gl Works with 60 dimensions', function(done) { var mockCopy = Lib.extendDeep({}, mock1); var newDimension, i, j; @@ -539,7 +539,7 @@ describe('parcoords edge cases', function() { .then(done); }); - it('@noCI @gl Truncates 60+ dimensions to 60', function(done) { + it('@gl Truncates 60+ dimensions to 60', function(done) { var mockCopy = Lib.extendDeep({}, mock1); var newDimension, i, j; @@ -567,7 +567,7 @@ describe('parcoords edge cases', function() { .then(done); }); - it('@noCI @gl Truncates dimension values to the shortest array, retaining only 3 lines', function(done) { + it('@gl Truncates dimension values to the shortest array, retaining only 3 lines', function(done) { var mockCopy = Lib.extendDeep({}, mock1); var newDimension, i, j; @@ -1218,7 +1218,7 @@ describe('parcoords basic use', function() { }); }); -describe('@noCI parcoords constraint interactions', function() { +describe('parcoords constraint interactions', function() { var gd, initialDashArray0, initialDashArray1; function initialFigure() { @@ -1302,7 +1302,7 @@ describe('@noCI parcoords constraint interactions', function() { expect(dashArray.length).toBe(segmentCount, dashArray); } - it('@gl snaps ordinal constraints', function(done) { + it('@noCI @gl snaps ordinal constraints', function(done) { // first: drag almost to 2 but not quite - constraint will snap back to [2.75, 4] mostOfDrag(105, 165, 105, 190); var newDashArray = getDashArray(0); @@ -1380,7 +1380,7 @@ describe('@noCI parcoords constraint interactions', function() { .then(done); }); - it('@gl updates continuous constraints with no snap', function(done) { + it('@noCI @gl updates continuous constraints with no snap', function(done) { // first: extend 7 to 5 mostOfDrag(295, 160, 295, 200); var newDashArray = getDashArray(1); diff --git a/test/jasmine/tests/plot_api_react_test.js b/test/jasmine/tests/plot_api_react_test.js index 2337ebbd766..e75f39a024a 100644 --- a/test/jasmine/tests/plot_api_react_test.js +++ b/test/jasmine/tests/plot_api_react_test.js @@ -700,7 +700,7 @@ describe('@noCIdep Plotly.react', function() { .then(done); }); - it('can change parcoords aggregations', function(done) { + it('@gl can change parcoords aggregations', function(done) { Plotly.newPlot(gd, aggregatedParcoords(0)) .then(checkValues(aggParcoords0Vals)) @@ -717,7 +717,7 @@ describe('@noCIdep Plotly.react', function() { .then(done); }); - it('can change type with aggregations', function(done) { + it('@gl can change type with aggregations', function(done) { Plotly.newPlot(gd, aggregatedScatter(1)) .then(checkCalcData(aggScatter1CD)) @@ -1766,7 +1766,7 @@ describe('Plotly.react and uirevision attributes', function() { _run(fig, editEditable, checkAttrs(true), checkAttrs).then(done); }); - it('preserves editable: true name, colorbar title and parcoords constraint range via trace.uirevision', function(done) { + it('@gl preserves editable: true name, colorbar title and parcoords constraint range via trace.uirevision', function(done) { function fig(mainRev, traceRev) { return { data: [{ diff --git a/test/jasmine/tests/polar_test.js b/test/jasmine/tests/polar_test.js index 5e78ae92c12..1896ad949dd 100644 --- a/test/jasmine/tests/polar_test.js +++ b/test/jasmine/tests/polar_test.js @@ -899,16 +899,12 @@ describe('Test polar interactions:', function() { } function _reset() { - return delay(100)() - .then(function() { return _doubleClick(mid); }) - .then(function() { - relayoutNumber++; - resetNumber++; + relayoutNumber++; + resetNumber++; - var extra = '(reset ' + resetNumber + ')'; - _assertBase(extra); - expect(eventCnts.plotly_doubleclick).toBe(resetNumber, 'doubleclick event #' + extra); - }); + var extra = '(reset ' + resetNumber + ')'; + _assertBase(extra); + expect(eventCnts.plotly_doubleclick).toBe(resetNumber, 'doubleclick event #' + extra); } _plot(fig) @@ -917,21 +913,33 @@ describe('Test polar interactions:', function() { .then(function() { _assertDrag([0, 5.24], 'from center move toward bottom-right'); }) + .then(delay(20)) + .then(function() { return _doubleClick(mid); }) + .then(delay(20)) .then(_reset) .then(function() { return _drag(mid, [-50, -50]); }) .then(function() { _assertDrag([0, 5.24], 'from center move toward top-left'); }) + .then(delay(20)) + .then(function() { return _doubleClick(mid); }) + .then(delay(20)) .then(_reset) .then(function() { return _drag([mid[0] + 30, mid[0] - 30], [50, -50]); }) .then(function() { _assertDrag([3.1, 8.4], 'from quadrant #1 move top-right'); }) + .then(delay(20)) + .then(function() { return _doubleClick(mid); }) + .then(delay(20)) .then(_reset) .then(function() { return _drag([345, 200], [-50, 0]); }) .then(function() { _assertDrag([7.0, 11.1], 'from right edge move left'); }) + .then(delay(20)) + .then(function() { return _doubleClick(mid); }) + .then(delay(20)) .then(_reset) .then(function() { return _drag(mid, [10, 10]);}) .then(function() { _assertBase('from center to not far enough'); }) @@ -943,6 +951,9 @@ describe('Test polar interactions:', function() { expect(eventCnts.plotly_relayout) .toBe(relayoutNumber, 'no new relayout events after *not far enough* cases'); }) + .then(delay(20)) + .then(function() { return _doubleClick(mid); }) + .then(delay(20)) .then(_reset) .then(function() { return Plotly.relayout(gd, 'polar.hole', 0.2); }) .then(function() { relayoutNumber++; }) diff --git a/test/jasmine/tests/select_test.js b/test/jasmine/tests/select_test.js index 5f788d6d80a..7c4ab025584 100644 --- a/test/jasmine/tests/select_test.js +++ b/test/jasmine/tests/select_test.js @@ -658,7 +658,7 @@ describe('Click-to-select', function() { { mapboxAccessToken: require('@build/credentials.json').MAPBOX_ACCESS_TOKEN }) ] .forEach(function(testCase) { - it('@noCI @gl trace type ' + testCase.label, function(done) { + it('@gl trace type ' + testCase.label, function(done) { _run(testCase, done); }); }); diff --git a/test/jasmine/tests/splom_test.js b/test/jasmine/tests/splom_test.js index 4df71226d61..20535070488 100644 --- a/test/jasmine/tests/splom_test.js +++ b/test/jasmine/tests/splom_test.js @@ -943,7 +943,7 @@ describe('Test splom interactions:', function() { .then(done); }); - it('@noCI @gl should clear graph and replot when canvas and WebGL context dimensions do not match', function(done) { + it('@gl should clear graph and replot when canvas and WebGL context dimensions do not match', function(done) { var fig = Lib.extendDeep({}, require('@mocks/splom_iris.json')); function assertDims(msg, w, h) { @@ -1737,7 +1737,7 @@ describe('Test splom select:', function() { .then(done); }); - it('@noCI @gl should behave correctly during select->dblclick->pan scenarios', function(done) { + it('@gl should behave correctly during select->dblclick->pan scenarios', function(done) { var fig = Lib.extendDeep({}, require('@mocks/splom_0.json')); fig.layout = { width: 400, From c57d31622706ad5af813a108b250d94c12bfc2b1 Mon Sep 17 00:00:00 2001 From: archmoj Date: Sun, 24 Mar 2019 12:38:49 -0400 Subject: [PATCH 2/7] moved gl2d double-click into another file reset timeout and reduced delays set gl retry to 3 --- .circleci/test.sh | 4 +- test/jasmine/tests/gl2d_click_test.js | 620 ------------------ test/jasmine/tests/gl2d_double_click_test.js | 638 +++++++++++++++++++ 3 files changed, 640 insertions(+), 622 deletions(-) create mode 100644 test/jasmine/tests/gl2d_double_click_test.js diff --git a/.circleci/test.sh b/.circleci/test.sh index 4dbabf8879f..78325917cf4 100755 --- a/.circleci/test.sh +++ b/.circleci/test.sh @@ -44,7 +44,7 @@ case $1 in jasmine) set_tz - npm run test-jasmine -- --skip-tags=gl,noCI,flaky || EXIT_STATE=$? + npm run test-jasmine -- --skip-tags=noCI,gl,flaky || EXIT_STATE=$? npm run test-bundle || EXIT_STATE=$? exit $EXIT_STATE @@ -54,7 +54,7 @@ case $1 in set_tz SHARDS=($(node $ROOT/tasks/shard_jasmine_tests.js --tag=gl)) - MAX_AUTO_RETRY=2 + MAX_AUTO_RETRY=3 for s in ${SHARDS[@]}; do retry npm run test-jasmine -- "${s}" --tags=gl --skip-tags=noCI done diff --git a/test/jasmine/tests/gl2d_click_test.js b/test/jasmine/tests/gl2d_click_test.js index 6167a7bc332..9d59f00eb53 100644 --- a/test/jasmine/tests/gl2d_click_test.js +++ b/test/jasmine/tests/gl2d_click_test.js @@ -14,11 +14,9 @@ var assertHoverLabelContent = customAssertions.assertHoverLabelContent; // from the mousemove events and then simulate // a click event on mouseup var click = require('../assets/timed_click'); -var doubleClick = require('../assets/double_click'); var hover = require('../assets/hover'); var delay = require('../assets/delay'); var mouseEvent = require('../assets/mouse_event'); -var readPixel = require('../assets/read_pixel'); // contourgl is not part of the dist plotly.js bundle initially Plotly.register([ @@ -560,621 +558,3 @@ describe('Test hover and click interactions', function() { .then(done); }); }); - -describe('Test gl2d lasso/select:', function() { - var mockFancy = require('@mocks/gl2d_14.json'); - delete mockFancy.layout.xaxis.autorange; - delete mockFancy.layout.yaxis.autorange; - mockFancy.layout.xaxis.range = [-2.951309064136961, 2.0954721318818916]; - mockFancy.layout.yaxis.range = [-0.9248866483012275, 1.3232607344525835]; - - var mockFast = Lib.extendDeep({}, mockFancy, { - data: [{mode: 'markers'}], - layout: { - xaxis: { - type: 'linear', - range: [-3.869222222222223, 73.55522222222223] - }, - yaxis: { - type: 'linear', - range: [-0.7402222222222222, 17.144222222222222] - } - } - }); - - var gd; - var selectPath = [[98, 193], [108, 193]]; - var selectPath2 = [[118, 193], [128, 193]]; - var lassoPath = [[316, 171], [318, 239], [335, 243], [328, 169]]; - var lassoPath2 = [[98, 193], [108, 193], [108, 500], [98, 500], [98, 193]]; - - afterEach(function() { - Plotly.purge(gd); - destroyGraphDiv(); - }); - - function drag(path) { - var len = path.length; - var el = d3.select(gd).select('rect.nsewdrag').node(); - var opts = {element: el}; - - Lib.clearThrottle(); - mouseEvent('mousemove', path[0][0], path[0][1], opts); - mouseEvent('mousedown', path[0][0], path[0][1], opts); - - path.slice(1, len).forEach(function(pt) { - Lib.clearThrottle(); - mouseEvent('mousemove', pt[0], pt[1], opts); - }); - - mouseEvent('mouseup', path[len - 1][0], path[len - 1][1], opts); - } - - function select(path) { - return new Promise(function(resolve, reject) { - gd.once('plotly_selected', resolve); - setTimeout(function() { reject('did not trigger *plotly_selected*');}, 300); - drag(path); - }); - } - - function assertEventData(actual, expected) { - expect(actual.points.length).toBe(expected.points.length); - - expected.points.forEach(function(e, i) { - var a = actual.points[i]; - if(a) { - expect(a.x).toBe(e.x, 'x'); - expect(a.y).toBe(e.y, 'y'); - } - }); - } - - - it('@gl should work under fast mode with *select* dragmode', function(done) { - var _mock = Lib.extendDeep({}, mockFast); - _mock.layout.dragmode = 'select'; - gd = createGraphDiv(); - - Plotly.plot(gd, _mock) - .then(delay(100)) - .then(function() { - expect(gd._fullLayout._plots.xy._scene.select2d).not.toBe(undefined, 'scatter2d renderer'); - - return select(selectPath); - }) - .then(delay(100)) - .then(function(eventData) { - assertEventData(eventData, { - points: [ - {pointNumber: 25, x: 1.425, y: 0.538}, - {pointNumber: 26, x: 1.753, y: 0.5}, - {pointNumber: 27, x: 2.22, y: 0.45} - ] - }); - - }) - .catch(failTest) - .then(done); - }); - - it('@gl should work under fast mode with *lasso* dragmode', function(done) { - var _mock = Lib.extendDeep({}, mockFast); - _mock.layout.dragmode = 'lasso'; - gd = createGraphDiv(); - - Plotly.plot(gd, _mock) - .then(delay(100)) - .then(function() { - return select(lassoPath2); - }) - .then(delay(100)) - .then(function(eventData) { - assertEventData(eventData, { - points: [ - {pointNumber: 25, x: 1.425, y: 0.538}, - {pointNumber: 26, x: 1.753, y: 0.5}, - {pointNumber: 27, x: 2.22, y: 0.45} - ] - }); - }) - .catch(failTest) - .then(done); - }); - - it('@gl should work under fancy mode with *select* dragmode', function(done) { - var _mock = Lib.extendDeep({}, mockFancy); - _mock.layout.dragmode = 'select'; - gd = createGraphDiv(); - - Plotly.plot(gd, _mock) - .then(delay(100)) - .then(function() { - return select(selectPath2); - }) - .then(delay(100)) - .then(function(eventData) { - assertEventData(eventData, { - points: [{x: 0.004, y: 12.5}] - }); - }) - .catch(failTest) - .then(done); - }); - - it('@gl should work under fancy mode with *lasso* dragmode', function(done) { - var _mock = Lib.extendDeep({}, mockFancy); - _mock.layout.dragmode = 'lasso'; - gd = createGraphDiv(); - - Plotly.plot(gd, _mock) - .then(delay(100)) - .then(function() { - return select(lassoPath); - }) - .then(function(eventData) { - assertEventData(eventData, { - points: [{ x: 0.099, y: 2.75 }] - }); - }) - .catch(failTest) - .then(done); - }); - - it('@gl should work on trace with enabled transforms', function(done) { - var fig = Lib.extendDeep({}, require('@mocks/gl2d_transforms.json')); - fig.layout.dragmode = 'select'; - fig.layout.margin = {t: 0, b: 0, l: 0, r: 0}; - fig.layout.height = 500; - fig.layout.width = 500; - gd = createGraphDiv(); - - Plotly.plot(gd, fig) - .then(delay(100)) - .then(function() { return select([[100, 100], [250, 250]]); }) - .then(function(eventData) { - assertEventData(eventData, { - points: [ - { x: 3, y: 4 }, - { x: 2, y: 4 } - ] - }); - }) - .catch(failTest) - .then(done); - }); - - it('@gl should work on gl text charts', function(done) { - var fig = Lib.extendDeep({}, require('@mocks/gl2d_text_chart_basic.json')); - fig.layout.dragmode = 'select'; - fig.layout.margin = {t: 0, b: 0, l: 0, r: 0}; - fig.layout.height = 500; - fig.layout.width = 500; - gd = createGraphDiv(); - - function _assertGlTextOpts(msg, exp) { - var scene = gd.calcdata[0][0].t._scene; - scene.glText.forEach(function(opts, i) { - expect(Array.from(opts.color)) - .toBeCloseToArray(exp.rgba[i], 2, 'item ' + i + ' - ' + msg); - }); - } - - Plotly.plot(gd, fig) - .then(delay(100)) - .then(function() { - _assertGlTextOpts('base', { - rgba: [ - [68, 68, 68, 255], - [68, 68, 68, 255], - [68, 68, 68, 255] - ] - }); - }) - .then(function() { return select([[100, 100], [250, 250]]); }) - .then(function(eventData) { - assertEventData(eventData, { - points: [{x: 1, y: 2}] - }); - _assertGlTextOpts('after selection', { - rgba: [ - [ - 68, 68, 68, 51, - 68, 68, 68, 51, - 68, 68, 68, 51, - ], - [ - 68, 68, 68, 51, - // this is the selected pt! - 68, 68, 68, 255, - 68, 68, 68, 51 - ], - [ - 68, 68, 68, 51, - 68, 68, 68, 51, - 68, 68, 68, 51 - ] - ] - }); - }) - .then(function() { - return Plotly.restyle(gd, 'selected.textfont.color', 'red'); - }) - .then(function() { return select([[100, 100], [250, 250]]); }) - .then(function() { - _assertGlTextOpts('after selection - with set selected.textfont.color', { - rgba: [ - [ - 68, 68, 68, 255, - 68, 68, 68, 255, - 68, 68, 68, 255, - ], - [ - 68, 68, 68, 255, - // this is the selected pt! - 255, 0, 0, 255, - 68, 68, 68, 255 - ], - [ - 68, 68, 68, 255, - 68, 68, 68, 255, - 68, 68, 68, 255 - ] - ] - }); - }) - .catch(failTest) - .then(done); - }); - - it('@gl should work on gl text charts with array textfont.color', function(done) { - var fig = Lib.extendDeep({}, require('@mocks/gl2d_text_chart_arrays.json')); - fig.layout.dragmode = 'select'; - fig.layout.margin = {t: 0, b: 0, l: 0, r: 0}; - fig.layout.height = 500; - fig.layout.width = 500; - gd = createGraphDiv(); - - function _assertGlTextOpts(msg, exp) { - var scene = gd.calcdata[0][0].t._scene; - scene.glText.forEach(function(opts, i) { - expect(Array.from(opts.color)) - .toBeCloseToArray(exp.rgba[i], 2, 'item ' + i + ' - ' + msg); - }); - } - - Plotly.plot(gd, fig) - .then(delay(100)) - .then(function() { - _assertGlTextOpts('base', { - rgba: [ - [ - 255, 0, 0, 255, - 0, 0, 255, 255, - 0, 128, 0, 255 - ], - [ - 0, 0, 0, 255, - 211, 211, 210, 255, - 237, 97, 0, 255 - ] - ] - }); - }) - .then(function() { return select([[100, 10], [250, 100]]); }) - .then(function(eventData) { - assertEventData(eventData, { - points: [{x: 1, y: 2}] - }); - _assertGlTextOpts('after selection', { - rgba: [ - [ - 255, 0, 0, 51, - 0, 0, 255, 51, - 0, 128, 0, 51 - ], - [ - 0, 0, 0, 51, - // this is the selected pt! - 211, 211, 210, 255, - 237, 97, 0, 51 - ] - ] - }); - }) - .then(function() { - return Plotly.restyle(gd, 'selected.textfont.color', 'red'); - }) - .then(function() { return select([[100, 10], [250, 100]]); }) - .then(function() { - _assertGlTextOpts('after selection - with set selected.textfont.color', { - rgba: [ - [ - 255, 0, 0, 255, - 0, 0, 255, 255, - 0, 128, 0, 255 - ], - [ - 0, 0, 0, 255, - // this is the selected pt! - 255, 0, 0, 255, - 237, 97, 0, 255 - ] - ] - }); - }) - .catch(failTest) - .then(done); - }); - - it('@gl should work after a width/height relayout', function(done) { - gd = createGraphDiv(); - - var w = 500; - var h = 500; - var w2 = 800; - var h2 = 600; - var pad = 20; - - function _read(query) { - var canvas = gd.querySelector(query); - return readPixel(canvas, 0, 0, gd.layout.width, gd.layout.height) - .reduce(function(acc, v) { return acc + v; }, 0); - } - - function readContext() { return _read('.gl-canvas-context'); } - - function readFocus() { return _read('.gl-canvas-focus'); } - - Plotly.plot(gd, [{ - type: 'scattergl', - mode: 'markers', - y: [2, 1, 2] - }], { - dragmode: 'select', - margin: {t: 0, b: 0, l: 0, r: 0}, - width: w, height: h - }) - .then(delay(100)) - .then(function() { - expect(readContext()).toBeGreaterThan(1e4, 'base context'); - expect(readFocus()).toBe(0, 'base focus'); - }) - .then(function() { return select([[pad, pad], [w - pad, h - pad]]); }) - .then(function() { - expect(readContext()).toBe(0, 'select context'); - expect(readFocus()).toBeGreaterThan(1e4, 'select focus'); - }) - .then(function() { - return Plotly.update(gd, - {selectedpoints: null}, - {width: w2, height: h2} - ); - }) - .then(function() { - expect(readContext()).toBeGreaterThan(1e4, 'update context'); - expect(readFocus()).toBe(0, 'update focus'); - }) - .then(function() { return select([[pad, pad], [w2 - pad, h2 - pad]]); }) - .then(function() { - // make sure full w2/h2 context canvas is cleared! - // from https://github.com/plotly/plotly.js/issues/2731 - expect(readContext()).toBe(0, 'update+select context'); - expect(readFocus()).toBeGreaterThan(1e4, 'update+select focus'); - }) - .catch(failTest) - .then(done); - }); - - it('@gl should behave correctly during select+doubleclick+pan scenarios', function(done) { - gd = createGraphDiv(); - - // See https://github.com/plotly/plotly.js/issues/2767 - - function grabScene() { - return gd.calcdata[0][0].t._scene; - } - - function _assert(msg, exp) { - var scene = grabScene(); - var scatter2d = scene.scatter2d; - - expect((scene.markerOptions || [])[0].opacity) - .toBe(1, 'marker.opacity - ' + msg); - expect((scene.markerSelectedOptions || [])[0].opacity) - .toBe(1, 'selected.marker.opacity - ' + msg); - expect((scene.markerUnselectedOptions || [])[0].opacity) - .toBe(0.2, 'unselected.marker.opacity - ' + msg); - - expect(scene.selectBatch).toEqual(exp.selectBatch); - expect(scene.unselectBatch).toEqual(exp.unselectBatch); - - var updateCalls = scatter2d.update.calls.all(); - var drawCalls = scatter2d.draw.calls.all(); - - expect(updateCalls.length).toBe( - exp.updateArgs.length, - 'scatter2d.update has been called the correct number of times - ' + msg - ); - updateCalls.forEach(function(d, i) { - d.args.forEach(function(arg, j) { - if('range' in arg[0]) { - // no need to assert range value in detail - expect(exp.updateArgs[i][j]).toBe( - 'range', - 'scatter.update range update - ' + msg - ); - } else { - expect(arg).toBe( - exp.updateArgs[i][j], - 'scatter.update call' + i + ' arg' + j + ' - ' + msg - ); - } - }); - }); - - expect(drawCalls.length).toBe( - exp.drawArgs.length, - 'scatter2d.draw has been called the correct number of times - ' + msg - ); - drawCalls.forEach(function(d, i) { - d.args.forEach(function(arg, j) { - expect(arg).toBe( - exp.drawArgs[i][j], - 'scatter.draw call' + i + ' arg' + j + ' - ' + msg - ); - }); - }); - - scene.scatter2d.update.calls.reset(); - scene.scatter2d.draw.calls.reset(); - } - - var unselectBatchOld; - - Plotly.newPlot('graph', [{ - type: 'scattergl', - mode: 'markers', - y: [1, 2, 1], - marker: {size: 30} - }], { - dragmode: 'select', - margin: {t: 0, b: 0, l: 0, r: 0}, - width: 500, - height: 500 - }) - .then(delay(100)) - .then(function() { - var scene = grabScene(); - spyOn(scene.scatter2d, 'update').and.callThrough(); - spyOn(scene.scatter2d, 'draw').and.callThrough(); - }) - .then(function() { - _assert('base', { - selectBatch: [], - unselectBatch: [], - updateArgs: [], - drawArgs: [] - }); - }) - .then(function() { return select([[20, 20], [480, 250]]); }) - .then(function() { - var scene = grabScene(); - _assert('after select', { - selectBatch: [[1]], - unselectBatch: [[0, 2]], - updateArgs: [ - // N.B. scatter2d now draws unselected options - [scene.markerUnselectedOptions], - ], - drawArgs: [ - [scene.unselectBatch] - ] - }); - }) - .then(function() { return doubleClick(250, 250); }) - .then(function() { - var scene = grabScene(); - _assert('after doubleclick', { - selectBatch: [null], - unselectBatch: [[0, 1, 2]], - updateArgs: [], - drawArgs: [ - // call in no-selection loop (can we get rid of this?) - [0], - // call with unselectBatch - [scene.unselectBatch] - ] - }); - }) - .then(function() { return Plotly.relayout(gd, 'dragmode', 'pan'); }) - .then(function() { - _assert('after relayout to *pan*', { - selectBatch: [null], - unselectBatch: [[0, 1, 2]], - // nothing to do when relayouting to 'pan' - updateArgs: [], - drawArgs: [] - }); - - // keep ref for next _assert() - var scene = grabScene(); - unselectBatchOld = scene.unselectBatch; - }) - .then(function() { return drag([[200, 200], [250, 250]]); }) - .then(function() { - var scene = grabScene(); - _assert('after pan', { - selectBatch: null, - unselectBatch: null, - // drag triggers: - // - 2 scene.update() calls, which each invoke - // + 1 scatter2d.update (updating viewport) - // + 2 scatter2d.draw (same as after double-click) - // - // replot on mouseup triggers: - // - 1 scatter2d.update updating viewport - // - 1 scatter2d.update resetting markerOptions - // - 1 (full) scene.draw() - updateArgs: [ - ['range'], - ['range'], - // N.B. bring scatter2d back to 'base' marker options - [scene.markerOptions], - ['range'] - ], - drawArgs: [ - [0], - [unselectBatchOld], - [0], - [unselectBatchOld], - [0] - ] - }); - }) - .catch(failTest) - .then(done); - }); - - it('@gl should work on overlaid subplots', function(done) { - gd = createGraphDiv(); - - var scene, scene2; - - Plotly.plot(gd, [{ - x: [1, 2, 3], - y: [40, 50, 60], - type: 'scattergl', - mode: 'markers' - }, { - x: [2, 3, 4], - y: [4, 5, 6], - yaxis: 'y2', - type: 'scattergl', - mode: 'markers' - }], { - xaxis: {domain: [0.2, 1]}, - yaxis2: {overlaying: 'y', side: 'left', position: 0}, - showlegend: false, - margin: {l: 0, t: 0, b: 0, r: 0}, - width: 400, - height: 400, - dragmode: 'select' - }) - .then(delay(100)) - .then(function() { - scene = gd._fullLayout._plots.xy._scene; - scene2 = gd._fullLayout._plots.xy2._scene; - - spyOn(scene.scatter2d, 'draw'); - spyOn(scene2.scatter2d, 'draw'); - }) - .then(function() { return select([[20, 20], [380, 250]]); }) - .then(function() { - expect(scene.scatter2d.draw).toHaveBeenCalledTimes(1); - expect(scene2.scatter2d.draw).toHaveBeenCalledTimes(1); - }) - .catch(failTest) - .then(done); - }); -}); diff --git a/test/jasmine/tests/gl2d_double_click_test.js b/test/jasmine/tests/gl2d_double_click_test.js new file mode 100644 index 00000000000..64029973d2e --- /dev/null +++ b/test/jasmine/tests/gl2d_double_click_test.js @@ -0,0 +1,638 @@ +var Plotly = require('@lib/index'); +var Lib = require('@src/lib'); + +var d3 = require('d3'); +var createGraphDiv = require('../assets/create_graph_div'); +var destroyGraphDiv = require('../assets/destroy_graph_div'); +var failTest = require('../assets/fail_test.js'); + +// cartesian click events events use the hover data +// from the mousemove events and then simulate +// a click event on mouseup +var doubleClick = require('../assets/double_click'); +var delay = require('../assets/delay'); +var mouseEvent = require('../assets/mouse_event'); +var readPixel = require('../assets/read_pixel'); + +// contourgl is not part of the dist plotly.js bundle initially +Plotly.register([ + require('@lib/contourgl') +]); + +describe('Test gl2d lasso/select:', function() { + var mockFancy = require('@mocks/gl2d_14.json'); + delete mockFancy.layout.xaxis.autorange; + delete mockFancy.layout.yaxis.autorange; + mockFancy.layout.xaxis.range = [-2.951309064136961, 2.0954721318818916]; + mockFancy.layout.yaxis.range = [-0.9248866483012275, 1.3232607344525835]; + + var mockFast = Lib.extendDeep({}, mockFancy, { + data: [{mode: 'markers'}], + layout: { + xaxis: { + type: 'linear', + range: [-3.869222222222223, 73.55522222222223] + }, + yaxis: { + type: 'linear', + range: [-0.7402222222222222, 17.144222222222222] + } + } + }); + + var gd; + var selectPath = [[98, 193], [108, 193]]; + var selectPath2 = [[118, 193], [128, 193]]; + var lassoPath = [[316, 171], [318, 239], [335, 243], [328, 169]]; + var lassoPath2 = [[98, 193], [108, 193], [108, 500], [98, 500], [98, 193]]; + + afterEach(function() { + Plotly.purge(gd); + destroyGraphDiv(); + }); + + function drag(path) { + var len = path.length; + var el = d3.select(gd).select('rect.nsewdrag').node(); + var opts = {element: el}; + + Lib.clearThrottle(); + mouseEvent('mousemove', path[0][0], path[0][1], opts); + mouseEvent('mousedown', path[0][0], path[0][1], opts); + + path.slice(1, len).forEach(function(pt) { + Lib.clearThrottle(); + mouseEvent('mousemove', pt[0], pt[1], opts); + }); + + mouseEvent('mouseup', path[len - 1][0], path[len - 1][1], opts); + } + + function select(path) { + return new Promise(function(resolve, reject) { + gd.once('plotly_selected', resolve); + setTimeout(function() { reject('did not trigger *plotly_selected*');}, 200); + drag(path); + }); + } + + function assertEventData(actual, expected) { + expect(actual.points.length).toBe(expected.points.length); + + expected.points.forEach(function(e, i) { + var a = actual.points[i]; + if(a) { + expect(a.x).toBe(e.x, 'x'); + expect(a.y).toBe(e.y, 'y'); + } + }); + } + + + it('@gl should work under fast mode with *select* dragmode', function(done) { + var _mock = Lib.extendDeep({}, mockFast); + _mock.layout.dragmode = 'select'; + gd = createGraphDiv(); + + Plotly.plot(gd, _mock) + .then(delay(20)) + .then(function() { + expect(gd._fullLayout._plots.xy._scene.select2d).not.toBe(undefined, 'scatter2d renderer'); + + return select(selectPath); + }) + .then(delay(20)) + .then(function(eventData) { + assertEventData(eventData, { + points: [ + {pointNumber: 25, x: 1.425, y: 0.538}, + {pointNumber: 26, x: 1.753, y: 0.5}, + {pointNumber: 27, x: 2.22, y: 0.45} + ] + }); + + }) + .catch(failTest) + .then(done); + }); + + it('@gl should work under fast mode with *lasso* dragmode', function(done) { + var _mock = Lib.extendDeep({}, mockFast); + _mock.layout.dragmode = 'lasso'; + gd = createGraphDiv(); + + Plotly.plot(gd, _mock) + .then(delay(20)) + .then(function() { + return select(lassoPath2); + }) + .then(delay(20)) + .then(function(eventData) { + assertEventData(eventData, { + points: [ + {pointNumber: 25, x: 1.425, y: 0.538}, + {pointNumber: 26, x: 1.753, y: 0.5}, + {pointNumber: 27, x: 2.22, y: 0.45} + ] + }); + }) + .catch(failTest) + .then(done); + }); + + it('@gl should work under fancy mode with *select* dragmode', function(done) { + var _mock = Lib.extendDeep({}, mockFancy); + _mock.layout.dragmode = 'select'; + gd = createGraphDiv(); + + Plotly.plot(gd, _mock) + .then(delay(20)) + .then(function() { + return select(selectPath2); + }) + .then(delay(20)) + .then(function(eventData) { + assertEventData(eventData, { + points: [{x: 0.004, y: 12.5}] + }); + }) + .catch(failTest) + .then(done); + }); + + it('@gl should work under fancy mode with *lasso* dragmode', function(done) { + var _mock = Lib.extendDeep({}, mockFancy); + _mock.layout.dragmode = 'lasso'; + gd = createGraphDiv(); + + Plotly.plot(gd, _mock) + .then(delay(20)) + .then(function() { + return select(lassoPath); + }) + .then(function(eventData) { + assertEventData(eventData, { + points: [{ x: 0.099, y: 2.75 }] + }); + }) + .catch(failTest) + .then(done); + }); + + it('@gl should work on trace with enabled transforms', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/gl2d_transforms.json')); + fig.layout.dragmode = 'select'; + fig.layout.margin = {t: 0, b: 0, l: 0, r: 0}; + fig.layout.height = 500; + fig.layout.width = 500; + gd = createGraphDiv(); + + Plotly.plot(gd, fig) + .then(delay(20)) + .then(function() { return select([[100, 100], [250, 250]]); }) + .then(function(eventData) { + assertEventData(eventData, { + points: [ + { x: 3, y: 4 }, + { x: 2, y: 4 } + ] + }); + }) + .catch(failTest) + .then(done); + }); + + it('@gl should work on gl text charts', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/gl2d_text_chart_basic.json')); + fig.layout.dragmode = 'select'; + fig.layout.margin = {t: 0, b: 0, l: 0, r: 0}; + fig.layout.height = 500; + fig.layout.width = 500; + gd = createGraphDiv(); + + function _assertGlTextOpts(msg, exp) { + var scene = gd.calcdata[0][0].t._scene; + scene.glText.forEach(function(opts, i) { + expect(Array.from(opts.color)) + .toBeCloseToArray(exp.rgba[i], 2, 'item ' + i + ' - ' + msg); + }); + } + + Plotly.plot(gd, fig) + .then(delay(20)) + .then(function() { + _assertGlTextOpts('base', { + rgba: [ + [68, 68, 68, 255], + [68, 68, 68, 255], + [68, 68, 68, 255] + ] + }); + }) + .then(function() { return select([[100, 100], [250, 250]]); }) + .then(function(eventData) { + assertEventData(eventData, { + points: [{x: 1, y: 2}] + }); + _assertGlTextOpts('after selection', { + rgba: [ + [ + 68, 68, 68, 51, + 68, 68, 68, 51, + 68, 68, 68, 51, + ], + [ + 68, 68, 68, 51, + // this is the selected pt! + 68, 68, 68, 255, + 68, 68, 68, 51 + ], + [ + 68, 68, 68, 51, + 68, 68, 68, 51, + 68, 68, 68, 51 + ] + ] + }); + }) + .then(function() { + return Plotly.restyle(gd, 'selected.textfont.color', 'red'); + }) + .then(function() { return select([[100, 100], [250, 250]]); }) + .then(function() { + _assertGlTextOpts('after selection - with set selected.textfont.color', { + rgba: [ + [ + 68, 68, 68, 255, + 68, 68, 68, 255, + 68, 68, 68, 255, + ], + [ + 68, 68, 68, 255, + // this is the selected pt! + 255, 0, 0, 255, + 68, 68, 68, 255 + ], + [ + 68, 68, 68, 255, + 68, 68, 68, 255, + 68, 68, 68, 255 + ] + ] + }); + }) + .catch(failTest) + .then(done); + }); + + it('@gl should work on gl text charts with array textfont.color', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/gl2d_text_chart_arrays.json')); + fig.layout.dragmode = 'select'; + fig.layout.margin = {t: 0, b: 0, l: 0, r: 0}; + fig.layout.height = 500; + fig.layout.width = 500; + gd = createGraphDiv(); + + function _assertGlTextOpts(msg, exp) { + var scene = gd.calcdata[0][0].t._scene; + scene.glText.forEach(function(opts, i) { + expect(Array.from(opts.color)) + .toBeCloseToArray(exp.rgba[i], 2, 'item ' + i + ' - ' + msg); + }); + } + + Plotly.plot(gd, fig) + .then(delay(20)) + .then(function() { + _assertGlTextOpts('base', { + rgba: [ + [ + 255, 0, 0, 255, + 0, 0, 255, 255, + 0, 128, 0, 255 + ], + [ + 0, 0, 0, 255, + 211, 211, 210, 255, + 237, 97, 0, 255 + ] + ] + }); + }) + .then(function() { return select([[100, 10], [250, 100]]); }) + .then(function(eventData) { + assertEventData(eventData, { + points: [{x: 1, y: 2}] + }); + _assertGlTextOpts('after selection', { + rgba: [ + [ + 255, 0, 0, 51, + 0, 0, 255, 51, + 0, 128, 0, 51 + ], + [ + 0, 0, 0, 51, + // this is the selected pt! + 211, 211, 210, 255, + 237, 97, 0, 51 + ] + ] + }); + }) + .then(function() { + return Plotly.restyle(gd, 'selected.textfont.color', 'red'); + }) + .then(function() { return select([[100, 10], [250, 100]]); }) + .then(function() { + _assertGlTextOpts('after selection - with set selected.textfont.color', { + rgba: [ + [ + 255, 0, 0, 255, + 0, 0, 255, 255, + 0, 128, 0, 255 + ], + [ + 0, 0, 0, 255, + // this is the selected pt! + 255, 0, 0, 255, + 237, 97, 0, 255 + ] + ] + }); + }) + .catch(failTest) + .then(done); + }); + + it('@gl should work after a width/height relayout', function(done) { + gd = createGraphDiv(); + + var w = 500; + var h = 500; + var w2 = 800; + var h2 = 600; + var pad = 20; + + function _read(query) { + var canvas = gd.querySelector(query); + return readPixel(canvas, 0, 0, gd.layout.width, gd.layout.height) + .reduce(function(acc, v) { return acc + v; }, 0); + } + + function readContext() { return _read('.gl-canvas-context'); } + + function readFocus() { return _read('.gl-canvas-focus'); } + + Plotly.plot(gd, [{ + type: 'scattergl', + mode: 'markers', + y: [2, 1, 2] + }], { + dragmode: 'select', + margin: {t: 0, b: 0, l: 0, r: 0}, + width: w, height: h + }) + .then(delay(20)) + .then(function() { + expect(readContext()).toBeGreaterThan(1e4, 'base context'); + expect(readFocus()).toBe(0, 'base focus'); + }) + .then(function() { return select([[pad, pad], [w - pad, h - pad]]); }) + .then(function() { + expect(readContext()).toBe(0, 'select context'); + expect(readFocus()).toBeGreaterThan(1e4, 'select focus'); + }) + .then(function() { + return Plotly.update(gd, + {selectedpoints: null}, + {width: w2, height: h2} + ); + }) + .then(function() { + expect(readContext()).toBeGreaterThan(1e4, 'update context'); + expect(readFocus()).toBe(0, 'update focus'); + }) + .then(function() { return select([[pad, pad], [w2 - pad, h2 - pad]]); }) + .then(function() { + // make sure full w2/h2 context canvas is cleared! + // from https://github.com/plotly/plotly.js/issues/2731 + expect(readContext()).toBe(0, 'update+select context'); + expect(readFocus()).toBeGreaterThan(1e4, 'update+select focus'); + }) + .catch(failTest) + .then(done); + }); + + it('@gl should behave correctly during select+doubleclick+pan scenarios', function(done) { + gd = createGraphDiv(); + + // See https://github.com/plotly/plotly.js/issues/2767 + + function grabScene() { + return gd.calcdata[0][0].t._scene; + } + + function _assert(msg, exp) { + var scene = grabScene(); + var scatter2d = scene.scatter2d; + + expect((scene.markerOptions || [])[0].opacity) + .toBe(1, 'marker.opacity - ' + msg); + expect((scene.markerSelectedOptions || [])[0].opacity) + .toBe(1, 'selected.marker.opacity - ' + msg); + expect((scene.markerUnselectedOptions || [])[0].opacity) + .toBe(0.2, 'unselected.marker.opacity - ' + msg); + + expect(scene.selectBatch).toEqual(exp.selectBatch); + expect(scene.unselectBatch).toEqual(exp.unselectBatch); + + var updateCalls = scatter2d.update.calls.all(); + var drawCalls = scatter2d.draw.calls.all(); + + expect(updateCalls.length).toBe( + exp.updateArgs.length, + 'scatter2d.update has been called the correct number of times - ' + msg + ); + updateCalls.forEach(function(d, i) { + d.args.forEach(function(arg, j) { + if('range' in arg[0]) { + // no need to assert range value in detail + expect(exp.updateArgs[i][j]).toBe( + 'range', + 'scatter.update range update - ' + msg + ); + } else { + expect(arg).toBe( + exp.updateArgs[i][j], + 'scatter.update call' + i + ' arg' + j + ' - ' + msg + ); + } + }); + }); + + expect(drawCalls.length).toBe( + exp.drawArgs.length, + 'scatter2d.draw has been called the correct number of times - ' + msg + ); + drawCalls.forEach(function(d, i) { + d.args.forEach(function(arg, j) { + expect(arg).toBe( + exp.drawArgs[i][j], + 'scatter.draw call' + i + ' arg' + j + ' - ' + msg + ); + }); + }); + + scene.scatter2d.update.calls.reset(); + scene.scatter2d.draw.calls.reset(); + } + + var unselectBatchOld; + + Plotly.newPlot('graph', [{ + type: 'scattergl', + mode: 'markers', + y: [1, 2, 1], + marker: {size: 30} + }], { + dragmode: 'select', + margin: {t: 0, b: 0, l: 0, r: 0}, + width: 500, + height: 500 + }) + .then(delay(20)) + .then(function() { + var scene = grabScene(); + spyOn(scene.scatter2d, 'update').and.callThrough(); + spyOn(scene.scatter2d, 'draw').and.callThrough(); + }) + .then(function() { + _assert('base', { + selectBatch: [], + unselectBatch: [], + updateArgs: [], + drawArgs: [] + }); + }) + .then(function() { return select([[20, 20], [480, 250]]); }) + .then(function() { + var scene = grabScene(); + _assert('after select', { + selectBatch: [[1]], + unselectBatch: [[0, 2]], + updateArgs: [ + // N.B. scatter2d now draws unselected options + [scene.markerUnselectedOptions], + ], + drawArgs: [ + [scene.unselectBatch] + ] + }); + }) + .then(function() { return doubleClick(250, 250); }) + .then(function() { + var scene = grabScene(); + _assert('after doubleclick', { + selectBatch: [null], + unselectBatch: [[0, 1, 2]], + updateArgs: [], + drawArgs: [ + // call in no-selection loop (can we get rid of this?) + [0], + // call with unselectBatch + [scene.unselectBatch] + ] + }); + }) + .then(function() { return Plotly.relayout(gd, 'dragmode', 'pan'); }) + .then(function() { + _assert('after relayout to *pan*', { + selectBatch: [null], + unselectBatch: [[0, 1, 2]], + // nothing to do when relayouting to 'pan' + updateArgs: [], + drawArgs: [] + }); + + // keep ref for next _assert() + var scene = grabScene(); + unselectBatchOld = scene.unselectBatch; + }) + .then(function() { return drag([[200, 200], [250, 250]]); }) + .then(function() { + var scene = grabScene(); + _assert('after pan', { + selectBatch: null, + unselectBatch: null, + // drag triggers: + // - 2 scene.update() calls, which each invoke + // + 1 scatter2d.update (updating viewport) + // + 2 scatter2d.draw (same as after double-click) + // + // replot on mouseup triggers: + // - 1 scatter2d.update updating viewport + // - 1 scatter2d.update resetting markerOptions + // - 1 (full) scene.draw() + updateArgs: [ + ['range'], + ['range'], + // N.B. bring scatter2d back to 'base' marker options + [scene.markerOptions], + ['range'] + ], + drawArgs: [ + [0], + [unselectBatchOld], + [0], + [unselectBatchOld], + [0] + ] + }); + }) + .catch(failTest) + .then(done); + }); + + it('@gl should work on overlaid subplots', function(done) { + gd = createGraphDiv(); + + var scene, scene2; + + Plotly.plot(gd, [{ + x: [1, 2, 3], + y: [40, 50, 60], + type: 'scattergl', + mode: 'markers' + }, { + x: [2, 3, 4], + y: [4, 5, 6], + yaxis: 'y2', + type: 'scattergl', + mode: 'markers' + }], { + xaxis: {domain: [0.2, 1]}, + yaxis2: {overlaying: 'y', side: 'left', position: 0}, + showlegend: false, + margin: {l: 0, t: 0, b: 0, r: 0}, + width: 400, + height: 400, + dragmode: 'select' + }) + .then(delay(20)) + .then(function() { + scene = gd._fullLayout._plots.xy._scene; + scene2 = gd._fullLayout._plots.xy2._scene; + + spyOn(scene.scatter2d, 'draw'); + spyOn(scene2.scatter2d, 'draw'); + }) + .then(function() { return select([[20, 20], [380, 250]]); }) + .then(function() { + expect(scene.scatter2d.draw).toHaveBeenCalledTimes(1); + expect(scene2.scatter2d.draw).toHaveBeenCalledTimes(1); + }) + .catch(failTest) + .then(done); + }); +}); From 8241bc8b22164683f2d265aa49d732c615498542 Mon Sep 17 00:00:00 2001 From: archmoj Date: Thu, 28 Mar 2019 21:20:59 -0400 Subject: [PATCH 3/7] reduced retries to 2 and 3 - marked one test as flaky --- .circleci/test.sh | 4 ++-- test/jasmine/tests/sankey_test.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/test.sh b/.circleci/test.sh index 02c26f76e79..5eb16488f07 100755 --- a/.circleci/test.sh +++ b/.circleci/test.sh @@ -54,7 +54,7 @@ case $1 in set_tz SHARDS=($(node $ROOT/tasks/shard_jasmine_tests.js --tag=gl | circleci tests split)) - MAX_AUTO_RETRY=3 + MAX_AUTO_RETRY=2 for s in ${SHARDS[@]}; do retry npm run test-jasmine -- "$s" --tags=gl --skip-tags=noCI --showSkipped done @@ -66,7 +66,7 @@ case $1 in set_tz SHARDS=($(node $ROOT/tasks/shard_jasmine_tests.js --tag=flaky | circleci tests split)) - MAX_AUTO_RETRY=5 + MAX_AUTO_RETRY=3 for s in ${SHARDS[@]}; do retry npm run test-jasmine -- "$s" --tags=flaky --skip-tags=noCI --showSkipped done diff --git a/test/jasmine/tests/sankey_test.js b/test/jasmine/tests/sankey_test.js index 1d8c3df04c3..3c2159ba426 100644 --- a/test/jasmine/tests/sankey_test.js +++ b/test/jasmine/tests/sankey_test.js @@ -1128,7 +1128,7 @@ describe('sankey tests', function() { .then(done); }); - it('should persist the position of every nodes after drag in attributes nodes.(x|y)', function(done) { + it('@flaky should persist the position of every nodes after drag in attributes nodes.(x|y)', function(done) { mockCopy.data[0].arrangement = arrangement; var move = [50, -50]; var nodes; From 230544f110f72b39e99839f524d350c4c0d0ad33 Mon Sep 17 00:00:00 2001 From: archmoj Date: Thu, 28 Mar 2019 22:39:58 -0400 Subject: [PATCH 4/7] no foreach may help run the CI --- test/jasmine/tests/sankey_test.js | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/test/jasmine/tests/sankey_test.js b/test/jasmine/tests/sankey_test.js index 3c2159ba426..d5330b0cf19 100644 --- a/test/jasmine/tests/sankey_test.js +++ b/test/jasmine/tests/sankey_test.js @@ -1053,16 +1053,24 @@ describe('sankey tests', function() { .then(done); }); - ['node', 'link'].forEach(function(obj) { - it('@flaky should not output hover/unhover event data when ' + obj + '.hoverinfo is skip', function(done) { - var fig = Lib.extendDeep({}, mock); + it('should not output hover/unhover event data when link.hoverinfo is skip', function(done) { + var fig = Lib.extendDeep({}, mock); - Plotly.plot(gd, fig) - .then(function() { return Plotly.restyle(gd, obj + '.hoverinfo', 'skip'); }) - .then(assertNoHoverEvents(obj)) - .catch(failTest) - .then(done); - }); + Plotly.plot(gd, fig) + .then(function() { return Plotly.restyle(gd, 'link.hoverinfo', 'skip'); }) + .then(assertNoHoverEvents('link')) + .catch(failTest) + .then(done); + }); + + it('should not output hover/unhover event data when node.hoverinfo is skip', function(done) { + var fig = Lib.extendDeep({}, mock); + + Plotly.plot(gd, fig) + .then(function() { return Plotly.restyle(gd, 'node.hoverinfo', 'skip'); }) + .then(assertNoHoverEvents('node')) + .catch(failTest) + .then(done); }); }); From 17b506815dc44ba90b1daf2d0891b822bebb2ab0 Mon Sep 17 00:00:00 2001 From: archmoj Date: Fri, 29 Mar 2019 10:35:55 -0400 Subject: [PATCH 5/7] should reset one sankey test - no need to flaky flag as it fails locally and on the CI set timeout for the rest of the tests in one suite added one noCI flag to the sankey test which is failing on the CI and even locally with flaky flag set flaky retry to 5 now that it runs fast --- .circleci/test.sh | 2 +- test/jasmine/tests/gl2d_plot_interact_test.js | 3 ++- test/jasmine/tests/sankey_test.js | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.circleci/test.sh b/.circleci/test.sh index 5eb16488f07..362fe8ce87a 100755 --- a/.circleci/test.sh +++ b/.circleci/test.sh @@ -66,7 +66,7 @@ case $1 in set_tz SHARDS=($(node $ROOT/tasks/shard_jasmine_tests.js --tag=flaky | circleci tests split)) - MAX_AUTO_RETRY=3 + MAX_AUTO_RETRY=5 for s in ${SHARDS[@]}; do retry npm run test-jasmine -- "$s" --tags=flaky --skip-tags=noCI --showSkipped done diff --git a/test/jasmine/tests/gl2d_plot_interact_test.js b/test/jasmine/tests/gl2d_plot_interact_test.js index 0db0f1d3586..66577ca05b9 100644 --- a/test/jasmine/tests/gl2d_plot_interact_test.js +++ b/test/jasmine/tests/gl2d_plot_interact_test.js @@ -20,6 +20,8 @@ function countCanvases() { return d3.selectAll('canvas').size(); } +jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; + describe('Test removal of gl contexts', function() { var gd; @@ -319,7 +321,6 @@ describe('Test gl2d plots', function() { var mock = require('@mocks/gl2d_10.json'); beforeEach(function() { - jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; gd = createGraphDiv(); }); diff --git a/test/jasmine/tests/sankey_test.js b/test/jasmine/tests/sankey_test.js index d5330b0cf19..1b547cfba68 100644 --- a/test/jasmine/tests/sankey_test.js +++ b/test/jasmine/tests/sankey_test.js @@ -1136,7 +1136,7 @@ describe('sankey tests', function() { .then(done); }); - it('@flaky should persist the position of every nodes after drag in attributes nodes.(x|y)', function(done) { + it('@noCI should persist the position of every nodes after drag in attributes nodes.(x|y)', function(done) { mockCopy.data[0].arrangement = arrangement; var move = [50, -50]; var nodes; From 305e0b3e92e50ee71b8a3d465ff775f0d0a4d6ac Mon Sep 17 00:00:00 2001 From: archmoj Date: Mon, 1 Apr 2019 10:54:28 -0400 Subject: [PATCH 6/7] set flaky retry number to 4 - before rerun 10 times --- .circleci/test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/test.sh b/.circleci/test.sh index 362fe8ce87a..0b197bb03a8 100755 --- a/.circleci/test.sh +++ b/.circleci/test.sh @@ -66,7 +66,7 @@ case $1 in set_tz SHARDS=($(node $ROOT/tasks/shard_jasmine_tests.js --tag=flaky | circleci tests split)) - MAX_AUTO_RETRY=5 + MAX_AUTO_RETRY=4 for s in ${SHARDS[@]}; do retry npm run test-jasmine -- "$s" --tags=flaky --skip-tags=noCI --showSkipped done From 24aafc81c698d60f26c4e73c592040874451b2f3 Mon Sep 17 00:00:00 2001 From: archmoj Date: Mon, 1 Apr 2019 14:28:49 -0400 Subject: [PATCH 7/7] Apply Etienne recommendations reset shard limit to 20 and retry to 5 revised timout as well as before and after functions in describe blocks in gl2d_plot_interact_test lower the shard limit for the non-gl test-jasmine commands --- .circleci/test.sh | 7 +++-- tasks/shard_jasmine_tests.js | 2 +- test/jasmine/tests/gl2d_plot_interact_test.js | 26 +++++++++++++++---- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/.circleci/test.sh b/.circleci/test.sh index 0b197bb03a8..050cb809b31 100755 --- a/.circleci/test.sh +++ b/.circleci/test.sh @@ -6,13 +6,13 @@ set +o pipefail ROOT=$(dirname $0)/.. EXIT_STATE=0 +MAX_AUTO_RETRY=5 log () { echo -e "\n$1" } # inspired by https://unix.stackexchange.com/a/82602 -MAX_AUTO_RETRY=1 retry () { local n=1 @@ -54,7 +54,6 @@ case $1 in set_tz SHARDS=($(node $ROOT/tasks/shard_jasmine_tests.js --tag=gl | circleci tests split)) - MAX_AUTO_RETRY=2 for s in ${SHARDS[@]}; do retry npm run test-jasmine -- "$s" --tags=gl --skip-tags=noCI --showSkipped done @@ -65,8 +64,8 @@ case $1 in jasmine3) set_tz - SHARDS=($(node $ROOT/tasks/shard_jasmine_tests.js --tag=flaky | circleci tests split)) - MAX_AUTO_RETRY=4 + SHARDS=($(node $ROOT/tasks/shard_jasmine_tests.js --limit=1 --tag=flaky | circleci tests split)) + for s in ${SHARDS[@]}; do retry npm run test-jasmine -- "$s" --tags=flaky --skip-tags=noCI --showSkipped done diff --git a/tasks/shard_jasmine_tests.js b/tasks/shard_jasmine_tests.js index 88d600cb883..9b5fbdf04ba 100644 --- a/tasks/shard_jasmine_tests.js +++ b/tasks/shard_jasmine_tests.js @@ -15,7 +15,7 @@ var argv = minimist(process.argv.slice(2), { limit: ['l'], }, default: { - limit: 1 + limit: 20 } }); diff --git a/test/jasmine/tests/gl2d_plot_interact_test.js b/test/jasmine/tests/gl2d_plot_interact_test.js index 3d1f8af3013..5ee7b040dc8 100644 --- a/test/jasmine/tests/gl2d_plot_interact_test.js +++ b/test/jasmine/tests/gl2d_plot_interact_test.js @@ -20,12 +20,11 @@ function countCanvases() { return d3.selectAll('canvas').size(); } -jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; - describe('Test removal of gl contexts', function() { var gd; beforeEach(function() { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; gd = createGraphDiv(); }); @@ -98,6 +97,7 @@ describe('Test gl plot side effects', function() { var gd; beforeEach(function() { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; gd = createGraphDiv(); }); @@ -320,6 +320,7 @@ describe('Test gl2d plots', function() { var mock = require('@mocks/gl2d_10.json'); beforeEach(function() { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; gd = createGraphDiv(); }); @@ -1274,9 +1275,19 @@ describe('Test gl2d plots', function() { }); describe('Test scattergl autorange:', function() { - afterEach(destroyGraphDiv); - describe('should return the same value as SVG scatter for ~small~ data', function() { + var gd; + + beforeEach(function() { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; + gd = createGraphDiv(); + }); + + afterEach(function() { + Plotly.purge(gd); + destroyGraphDiv(); + }); + var specs = [ {name: 'lines+markers', fig: require('@mocks/gl2d_10.json')}, {name: 'bubbles', fig: require('@mocks/gl2d_12.json')}, @@ -1287,7 +1298,6 @@ describe('Test scattergl autorange:', function() { specs.forEach(function(s) { it('@gl - case ' + s.name, function(done) { - var gd = createGraphDiv(); var glRangeX; var glRangeY; @@ -1320,6 +1330,7 @@ describe('Test scattergl autorange:', function() { var gd; beforeEach(function() { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; gd = createGraphDiv(); // to avoid expansive draw calls (which could be problematic on CI) spyOn(ScatterGl, 'plot').and.callFake(function(gd) { @@ -1328,6 +1339,11 @@ describe('Test scattergl autorange:', function() { }); }); + afterEach(function() { + Plotly.purge(gd); + destroyGraphDiv(); + }); + // threshold for 'fast' axis expansion routine var N = 1e5; var x = new Array(N);