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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/plots/cartesian/graph_interact.js
Original file line numberDiff line numberDiff line change
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
Copy Markdown
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
Copy Markdown
Collaborator

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
Copy Markdown
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
Original file line numberDiff line numberDiff line change
@@ -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
Copy Markdown
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

varmockData,mockLayoutdescribe('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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Collaborator

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
Copy Markdown
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
Copy Markdown
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
Original file line numberDiff line numberDiff line change
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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/plots/cartesian/graph_interact.js
Original file line numberDiff line numberDiff line change
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
Copy Markdown
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
Copy Markdown
Collaborator

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
Copy Markdown
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
Original file line numberDiff line numberDiff line change
@@ -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
Copy Markdown
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

varmockData,mockLayoutdescribe('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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Collaborator

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
Copy Markdown
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
Copy Markdown
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
Original file line numberDiff line numberDiff line change
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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/plots/cartesian/graph_interact.js
Original file line numberDiff line numberDiff line change
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
Copy Markdown
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
Copy Markdown
Collaborator

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
Copy Markdown
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
Original file line numberDiff line numberDiff line change
@@ -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
Copy Markdown
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

varmockData,mockLayoutdescribe('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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Collaborator

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
Copy Markdown
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
Copy Markdown
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
Original file line numberDiff line numberDiff line change
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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/plots/cartesian/graph_interact.js
Original file line numberDiff line numberDiff line change
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
Copy Markdown
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
Copy Markdown
Collaborator

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
Copy Markdown
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
Original file line numberDiff line numberDiff line change
@@ -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
Copy Markdown
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

varmockData,mockLayoutdescribe('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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Collaborator

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
Copy Markdown
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
Copy Markdown
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
Original file line numberDiff line numberDiff line change
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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/plots/cartesian/graph_interact.js
Original file line numberDiff line numberDiff line change
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
Copy Markdown
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
Copy Markdown
Collaborator

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
Copy Markdown
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
Original file line numberDiff line numberDiff line change
@@ -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
Copy Markdown
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

varmockData,mockLayoutdescribe('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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Collaborator

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
Copy Markdown
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
Copy Markdown
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
Original file line numberDiff line numberDiff line change
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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/plots/cartesian/graph_interact.js
Original file line numberDiff line numberDiff line change
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
Copy Markdown
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
Copy Markdown
Collaborator

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
Copy Markdown
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
Original file line numberDiff line numberDiff line change
@@ -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
Copy Markdown
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

varmockData,mockLayoutdescribe('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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Collaborator

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
Copy Markdown
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
Copy Markdown
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
Original file line numberDiff line numberDiff line change
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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/plots/cartesian/graph_interact.js
Original file line numberDiff line numberDiff line change
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
Copy Markdown
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
Copy Markdown
Collaborator

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
Copy Markdown
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
Original file line numberDiff line numberDiff line change
@@ -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
Copy Markdown
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

varmockData,mockLayoutdescribe('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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Collaborator

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
Copy Markdown
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
Copy Markdown
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
Original file line numberDiff line numberDiff line change
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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/plots/cartesian/graph_interact.js
Original file line numberDiff line numberDiff line change
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
Copy Markdown
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
Copy Markdown
Collaborator

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
Copy Markdown
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
Original file line numberDiff line numberDiff line change
@@ -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
Copy Markdown
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

varmockData,mockLayoutdescribe('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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Collaborator

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
Copy Markdown
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
Copy Markdown
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
Original file line numberDiff line numberDiff line change
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