Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hover text bug #70

Merged
merged 5 commits into from Dec 4, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/plots/cartesian/graph_interact.js
Expand Up @@ -778,8 +778,9 @@ function createHoverText(hoverData, opts) {
var i, traceHoverinfo;
for (i = 0; i < hoverData.length; i++) {
traceHoverinfo = hoverData[i].trace.hoverinfo;
if (traceHoverinfo.indexOf('all')===-1 &&
traceHoverinfo.indexOf(hovermode)===-1) {
var parts = traceHoverinfo.split('+');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexcjohnson this has me thinking that perhaps flaglist attributes should be stored in fullData as arrays instead of strings, to make comparison more seamless. Moreover, maybe the all flag should be expected to e.g. x+y+text+name in the default step.

Alternatively, we could cook a nice general lib function isInFlagList that would take care of the logic on demand.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like isInFlagList better (I like it a lot, in fact 👍 ) - fullData isn't supposed to change things from data unless they're invalid, it's just supposed to fill in the blanks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great. Let's keep that in mind when we get to #34. There are only a few flaglist attributes at the moment, but more are coming.

if (parts.indexOf('all') === -1 &&
parts.indexOf(hovermode) === -1) {
showCommonLabel = false;
break;
}
Expand Down
245 changes: 245 additions & 0 deletions test/jasmine/tests/hover_label_test.js
@@ -0,0 +1,245 @@
var d3 = require('d3');

var Plotly = require('@src/index');
var Fx = require('@src/plots/cartesian/graph_interact');
var Lib = require('@src/lib');

var createGraphDiv = require('../assets/create_graph_div');
var destroyGraphDiv = require('../assets/destroy_graph_div');

describe('hover info', function() {
'use strict';

var mock = require('@mocks/14.json'),
evt = {
clientX: mock.layout.width/ 2,
clientY: mock.layout.height / 2
};

afterEach(destroyGraphDiv);

describe('hover info', function() {
var mockCopy = Lib.extendDeep({}, mock);

beforeEach(function(done) {
Plotly.plot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like we could have 1 beforeEach and multiple its?
pseudocode

var mockData, mockLayout

describe('hover tests', function () {
   mockData = mockData()
   mockLayout = mockLayout()

   beforeEach( function (done) {
       Plotly.plot(gd, mockData, mockLayout).then(done)
   })

   it('responds to hover', function () { // test that uses mockData and mockLayout }
   it('resonds to hover x', function () { // test ... }
})

We can put the var declarations global scope and then assign fresh copies in the single BeforeEach. No need for multiple describes and beforeEach's. If that is dooable is should really clear up the test.

For comparison check out the many Jest (Jasmine fork) tests in Filewell. We have a set of patterns that might be useful. https://github.com/plotly/streambed/blob/master/shelly/filewell/static/filewell/src/stores/__tests__/DirectoryStore-test.js

Here there is one BeforeEach to refresh state before each test and a few describe blocks that break the test into logical chunks. If we are just testing hover here one describe is probably enough

Check around the filewell src tree, every folder has a __test__ folder with Jest tests.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nop. That won't because we call Plotly.plot with different data each time.

We could only force update hovermode in gd directly without a plot call and test the labels then. But that feels kind of dirty.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah aight - the only other suggestion I can think of is to remove the multiple BeforeEach and Describes and do something like

it('does hover things', function(done) {
    Plotly.plot(newGD(), newData(), newLayout()).then( function () {
       expect(thingsToHappen()).toBe(true)
       done()
    })
})

where you get shorter code but have 1 level of nesting in the tests.

});

it('responds to hover', function() {
var gd = document.getElementById('graph');
Fx.hover('graph', evt, 'xy');

var hoverTrace = gd._hoverdata[0];

expect(hoverTrace.curveNumber).toEqual(0);
expect(hoverTrace.pointNumber).toEqual(17);
expect(hoverTrace.x).toEqual(0.388);
expect(hoverTrace.y).toEqual(1);

expect(d3.selectAll('g.axistext').size()).toEqual(1);
expect(d3.selectAll('g.hovertext').size()).toEqual(1);
expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0.388');
expect(d3.selectAll('g.hovertext').select('text').html()).toEqual('1');
});
});

describe('hover info x', function() {
var mockCopy = Lib.extendDeep({}, mock);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another note on jasmine: the describes happen in parallel. If an object need to be mutated from one it to another, make a copy of it in each it.


mockCopy.data[0].hoverinfo = 'x';

beforeEach(function(done) {
Plotly.plot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done);
});

it('responds to hover x', function() {
var gd = document.getElementById('graph');
Fx.hover('graph', evt, 'xy');

var hoverTrace = gd._hoverdata[0];

expect(hoverTrace.curveNumber).toEqual(0);
expect(hoverTrace.pointNumber).toEqual(17);
expect(hoverTrace.x).toEqual(0.388);
expect(hoverTrace.y).toEqual(1);

expect(d3.selectAll('g.axistext').size()).toEqual(1);
expect(d3.selectAll('g.hovertext').size()).toEqual(0);
expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0.388');
});
});

describe('hover info y', function() {
var mockCopy = Lib.extendDeep({}, mock);

mockCopy.data[0].hoverinfo = 'y';

beforeEach(function(done) {
Plotly.plot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexcjohnson @bpostlethwaite @mdtusz a note on jasmine async testing: it's not very good.

For example, it can't assert an expect inside a .then().

One way around this problem is to make all the async stuff happen in a beforeEach passing in the done callback which assures that the its below are run after the Plotly.plot has completed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great trick to know! But is there actually anything async in this call? I thought it was sync unless it had gl or mathjax?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jasmine supports async as of 2.0. The done callback in passed in every test.
http://jasmine.github.io/2.0/introduction.html#section-Asynchronous_Support

Won't that work for our needs?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bpostlethwaite Sure. Having setTimeouts in the it's could be another way around.

Not sure what's better / cleaner.

});

it('responds to hover y', function() {
var gd = document.getElementById('graph');
Fx.hover('graph', evt, 'xy');

var hoverTrace = gd._hoverdata[0];

expect(hoverTrace.curveNumber).toEqual(0);
expect(hoverTrace.pointNumber).toEqual(17);
expect(hoverTrace.x).toEqual(0.388);
expect(hoverTrace.y).toEqual(1);

expect(d3.selectAll('g.axistext').size()).toEqual(0);
expect(d3.selectAll('g.hovertext').size()).toEqual(1);
expect(d3.selectAll('g.hovertext').select('text').html()).toEqual('1');
});
});

describe('hover info text', function() {
var mockCopy = Lib.extendDeep({}, mock);

mockCopy.data[0].text = []
mockCopy.data[0].text[17] = 'hover text'
mockCopy.data[0].hoverinfo = 'text';

beforeEach(function(done) {
Plotly.plot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done);
});

it('responds to hover text', function() {
var gd = document.getElementById('graph');
Fx.hover('graph', evt, 'xy');

var hoverTrace = gd._hoverdata[0];

expect(hoverTrace.curveNumber).toEqual(0);
expect(hoverTrace.pointNumber).toEqual(17);
expect(hoverTrace.x).toEqual(0.388);
expect(hoverTrace.y).toEqual(1);

expect(d3.selectAll('g.axistext').size()).toEqual(0);
expect(d3.selectAll('g.hovertext').size()).toEqual(1);
expect(d3.selectAll('g.hovertext').select('text').html()).toEqual('hover text');
});
});

describe('hover info all', function() {
var mockCopy = Lib.extendDeep({}, mock);

mockCopy.data[0].text = []
mockCopy.data[0].text[17] = 'hover text'
mockCopy.data[0].hoverinfo = 'all';

beforeEach(function(done) {
Plotly.plot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done);
});

it('responds to hover all', function() {
var gd = document.getElementById('graph');
Fx.hover('graph', evt, 'xy');

var hoverTrace = gd._hoverdata[0];

expect(hoverTrace.curveNumber).toEqual(0);
expect(hoverTrace.pointNumber).toEqual(17);
expect(hoverTrace.x).toEqual(0.388);
expect(hoverTrace.y).toEqual(1);

expect(d3.selectAll('g.axistext').size()).toEqual(1);
expect(d3.selectAll('g.hovertext').size()).toEqual(1);
expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0.388');
expect(d3.selectAll('g.hovertext').select('text').selectAll('tspan').size()).toEqual(2);
expect(d3.selectAll('g.hovertext').selectAll('tspan')[0][0].innerHTML).toEqual('1')
expect(d3.selectAll('g.hovertext').selectAll('tspan')[0][1].innerHTML).toEqual('hover text')
});
});

describe('hover info y+text', function() {
var mockCopy = Lib.extendDeep({}, mock);

mockCopy.data[0].text = []
mockCopy.data[0].text[17] = 'hover text'
mockCopy.data[0].hoverinfo = 'y+text';

beforeEach(function(done) {
Plotly.plot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done);
});

it('responds to hover y+text', function() {
var gd = document.getElementById('graph');
Fx.hover('graph', evt, 'xy');

var hoverTrace = gd._hoverdata[0];

expect(hoverTrace.curveNumber).toEqual(0);
expect(hoverTrace.pointNumber).toEqual(17);
expect(hoverTrace.x).toEqual(0.388);
expect(hoverTrace.y).toEqual(1);

expect(d3.selectAll('g.axistext').size()).toEqual(0);
expect(d3.selectAll('g.hovertext').size()).toEqual(1);
expect(d3.selectAll('g.hovertext').selectAll('tspan').size()).toEqual(2);
expect(d3.selectAll('g.hovertext').selectAll('tspan')[0][0].innerHTML).toEqual('1')
expect(d3.selectAll('g.hovertext').selectAll('tspan')[0][1].innerHTML).toEqual('hover text')
});
});

describe('hover info x+text', function() {
var mockCopy = Lib.extendDeep({}, mock);

mockCopy.data[0].text = []
mockCopy.data[0].text[17] = 'hover text'
mockCopy.data[0].hoverinfo = 'x+text';

beforeEach(function(done) {
Plotly.plot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done);
});

it('responds to hover x+text', function() {
var gd = document.getElementById('graph');
Fx.hover('graph', evt, 'xy');

var hoverTrace = gd._hoverdata[0];

expect(hoverTrace.curveNumber).toEqual(0);
expect(hoverTrace.pointNumber).toEqual(17);
expect(hoverTrace.x).toEqual(0.388);
expect(hoverTrace.y).toEqual(1);

expect(d3.selectAll('g.axistext').size()).toEqual(1);
expect(d3.selectAll('g.hovertext').size()).toEqual(1);
expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0.388');
expect(d3.selectAll('g.hovertext').select('text').html()).toEqual('hover text');
});
});

describe('hover info text with html', function() {
var mockCopy = Lib.extendDeep({}, mock);

mockCopy.data[0].text = []
mockCopy.data[0].text[17] = 'hover<br>text'
mockCopy.data[0].hoverinfo = 'text';

beforeEach(function(done) {
Plotly.plot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done);
});

it('responds to hover text with html', function() {
var gd = document.getElementById('graph');
Fx.hover('graph', evt, 'xy');

var hoverTrace = gd._hoverdata[0];

expect(hoverTrace.curveNumber).toEqual(0);
expect(hoverTrace.pointNumber).toEqual(17);
expect(hoverTrace.x).toEqual(0.388);
expect(hoverTrace.y).toEqual(1);

expect(d3.selectAll('g.axistext').size()).toEqual(0);
expect(d3.selectAll('g.hovertext').size()).toEqual(1);
expect(d3.selectAll('g.hovertext').selectAll('tspan')[0][0].innerHTML).toEqual('hover')
expect(d3.selectAll('g.hovertext').selectAll('tspan')[0][1].innerHTML).toEqual('text')
expect(d3.selectAll('g.hovertext').select('text').selectAll('tspan').size()).toEqual(2);
});
});
});
22 changes: 0 additions & 22 deletions test/jasmine/tests/plot_interact_test.js
Expand Up @@ -45,27 +45,6 @@ describe('Test plot structure', function () {

expect(nodes[0].length).toEqual(Npts);
});

it('responds to hover', function() {
var gd = document.getElementById('graph');

var evt = {
clientX: gd.layout.width/ 2,
clientY: gd.layout.height / 2
};

Fx.hover('graph', evt, 'xy');

var hoverTrace = gd._hoverdata[0];

expect(hoverTrace.curveNumber).toEqual(0);
expect(hoverTrace.pointNumber).toEqual(17);
expect(hoverTrace.x).toEqual(0.388);
expect(hoverTrace.y).toEqual(1);

expect(d3.selectAll('g.axistext')[0].length).toEqual(1);
expect(d3.selectAll('g.hovertext')[0].length).toEqual(1);
});
});

describe('pie traces', function() {
Expand All @@ -86,7 +65,6 @@ describe('Test plot structure', function () {
expect(nodes[0].length).toEqual(Npts);
});
});

});

describe('geo plots', function() {
Expand Down