Skip to content

Commit 9520943

Browse files
committed
first pass 3d annotations jasmine tests
1 parent 1bbabad commit 9520943

File tree

1 file changed

+333
-1
lines changed

1 file changed

+333
-1
lines changed

test/jasmine/tests/gl_plot_interact_test.js

Lines changed: 333 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1284,7 +1284,6 @@ describe('Test gl2d interactions', function() {
12841284
});
12851285

12861286
it('data-referenced annotations should update on drag', function(done) {
1287-
12881287
function drag(start, end) {
12891288
mouseEvent('mousemove', start[0], start[1]);
12901289
mouseEvent('mousedown', start[0], start[1], { buttons: 1 });
@@ -1329,3 +1328,336 @@ describe('Test gl2d interactions', function() {
13291328
.then(done);
13301329
});
13311330
});
1331+
1332+
describe('Test gl3d annotations', function() {
1333+
var gd;
1334+
1335+
beforeAll(function() {
1336+
jasmine.addMatchers(customMatchers);
1337+
});
1338+
1339+
beforeEach(function() {
1340+
gd = createGraphDiv();
1341+
});
1342+
1343+
afterEach(function() {
1344+
Plotly.purge(gd);
1345+
destroyGraphDiv();
1346+
});
1347+
1348+
function assertAnnotationText(expectations, msg) {
1349+
var anns = d3.selectAll('g.annotation-text-g');
1350+
1351+
expect(anns.size()).toBe(expectations.length, msg);
1352+
1353+
anns.each(function(_, i) {
1354+
var tx = d3.select(this).select('text').text();
1355+
expect(tx).toEqual(expectations[i], msg + ' - ann ' + i);
1356+
});
1357+
}
1358+
1359+
function assertAnnotationsXY(expectations, msg) {
1360+
var TOL = 1.5;
1361+
var anns = d3.selectAll('g.annotation-text-g');
1362+
1363+
expect(anns.size()).toBe(expectations.length, msg);
1364+
1365+
anns.each(function(_, i) {
1366+
var ann = d3.select(this).select('g');
1367+
var translate = Drawing.getTranslate(ann);
1368+
1369+
expect(translate.x).toBeWithin(expectations[i][0], TOL, msg + ' - ann ' + i + ' x');
1370+
expect(translate.y).toBeWithin(expectations[i][1], TOL, msg + ' - ann ' + i + ' y');
1371+
});
1372+
}
1373+
1374+
// more robust (especially on CI) than update camera via mouse events
1375+
function updateCamera(x, y, z) {
1376+
return new Promise(function(resolve) {
1377+
var scene = gd._fullLayout.scene._scene;
1378+
var camera = scene.getCamera();
1379+
1380+
camera.eye = {x: x, y: y, z: z};
1381+
scene.setCamera(camera);
1382+
1383+
setTimeout(resolve, 100);
1384+
});
1385+
}
1386+
1387+
it('should move with camera', function(done) {
1388+
Plotly.plot(gd, [{
1389+
type: 'scatter3d',
1390+
x: [1, 2, 3],
1391+
y: [1, 2, 3],
1392+
z: [1, 2, 1]
1393+
}], {
1394+
scene: {
1395+
camera: {eye: {x: 2.1, y: 0.1, z: 0.9}},
1396+
annotations: [{
1397+
text: 'hello',
1398+
x: 1, y: 1, z: 1
1399+
}, {
1400+
text: 'sup?',
1401+
x: 1, y: 1, z: 2
1402+
}, {
1403+
text: 'look!',
1404+
x: 2, y: 2, z: 1
1405+
}]
1406+
}
1407+
})
1408+
.then(function() {
1409+
assertAnnotationsXY([[262, 199], [257, 135], [325, 233]], 'base 0');
1410+
1411+
return updateCamera(1.5, 2.5, 1.5);
1412+
})
1413+
.then(function() {
1414+
assertAnnotationsXY([[340, 187], [341, 142], [325, 221]], 'after camera update');
1415+
1416+
return updateCamera(2.1, 0.1, 0.9);
1417+
})
1418+
.then(function() {
1419+
assertAnnotationsXY([[262, 199], [257, 135], [325, 233]], 'base 0');
1420+
})
1421+
.catch(fail)
1422+
.then(done);
1423+
});
1424+
1425+
it('should be removed when beyond the scene axis ranges', function(done) {
1426+
var mock = Lib.extendDeep({}, require('@mocks/gl3d_annotations'));
1427+
1428+
// replace text with something easier to identify
1429+
mock.layout.scene.annotations.forEach(function(ann, i) { ann.text = String(i); });
1430+
1431+
Plotly.plot(gd, mock).then(function() {
1432+
assertAnnotationText(['0', '1', '2', '3'], 'base');
1433+
1434+
return Plotly.relayout(gd, 'scene.yaxis.range', [0.5, 1.5]);
1435+
})
1436+
.then(function() {
1437+
assertAnnotationText(['1'], 'after yaxis range relayout');
1438+
1439+
return Plotly.relayout(gd, 'scene.yaxis.range', null);
1440+
})
1441+
.then(function() {
1442+
assertAnnotationText(['0', '1', '2', '3'], 'back to base after yaxis range relayout');
1443+
1444+
return Plotly.relayout(gd, 'scene.zaxis.range', [0, 3]);
1445+
})
1446+
.then(function() {
1447+
assertAnnotationText(['0'], 'after zaxis range relayout');
1448+
1449+
return Plotly.relayout(gd, 'scene.zaxis.range', null);
1450+
})
1451+
.then(function() {
1452+
assertAnnotationText(['0', '1', '2', '3'], 'back to base after zaxis range relayout');
1453+
})
1454+
.catch(fail)
1455+
.then(done);
1456+
});
1457+
1458+
it('should be able to add/remove and hide/unhide themselves via relayout', function(done) {
1459+
var mock = Lib.extendDeep({}, require('@mocks/gl3d_annotations'));
1460+
1461+
// replace text with something easier to identify
1462+
mock.layout.scene.annotations.forEach(function(ann, i) { ann.text = String(i); });
1463+
1464+
var annNew = {
1465+
x: '2017-03-01',
1466+
y: 'C',
1467+
z: 3,
1468+
text: 'new!'
1469+
};
1470+
1471+
Plotly.plot(gd, mock).then(function() {
1472+
assertAnnotationText(['0', '1', '2', '3'], 'base');
1473+
1474+
return Plotly.relayout(gd, 'scene.annotations[1].visible', false);
1475+
})
1476+
.then(function() {
1477+
assertAnnotationText(['0', '2', '3'], 'after [1].visible:false');
1478+
1479+
return Plotly.relayout(gd, 'scene.annotations[1].visible', true);
1480+
})
1481+
.then(function() {
1482+
assertAnnotationText(['0', '1', '2', '3'], 'back to base (1)');
1483+
1484+
return Plotly.relayout(gd, 'scene.annotations[0]', null);
1485+
})
1486+
.then(function() {
1487+
assertAnnotationText(['1', '2', '3'], 'after [0] null');
1488+
1489+
return Plotly.relayout(gd, 'scene.annotations[0]', annNew);
1490+
})
1491+
.then(function() {
1492+
assertAnnotationText(['new!', '1', '2', '3'], 'after add new (1)');
1493+
1494+
return Plotly.relayout(gd, 'scene.annotations', null);
1495+
})
1496+
.then(function() {
1497+
assertAnnotationText([], 'after rm all');
1498+
1499+
return Plotly.relayout(gd, 'scene.annotations[0]', annNew);
1500+
})
1501+
.then(function() {
1502+
assertAnnotationText(['new!'], 'after add new (2)');
1503+
})
1504+
.catch(fail)
1505+
.then(done);
1506+
});
1507+
1508+
it('should work across multiple scenes', function(done) {
1509+
function assertAnnotationCntPerScene(id, cnt) {
1510+
expect(d3.selectAll('g.annotation-' + id).size()).toEqual(cnt);
1511+
}
1512+
1513+
Plotly.plot(gd, [{
1514+
type: 'scatter3d',
1515+
x: [1, 2, 3],
1516+
y: [1, 2, 3],
1517+
z: [1, 2, 1]
1518+
}, {
1519+
type: 'scatter3d',
1520+
x: [1, 2, 3],
1521+
y: [1, 2, 3],
1522+
z: [2, 1, 2],
1523+
scene: 'scene2'
1524+
}], {
1525+
scene: {
1526+
annotations: [{
1527+
text: 'hello',
1528+
x: 1, y: 1, z: 1
1529+
}]
1530+
},
1531+
scene2: {
1532+
annotations: [{
1533+
text: 'sup?',
1534+
x: 1, y: 1, z: 2
1535+
}, {
1536+
text: 'look!',
1537+
x: 2, y: 2, z: 1
1538+
}]
1539+
}
1540+
})
1541+
.then(function() {
1542+
assertAnnotationCntPerScene('scene', 1);
1543+
assertAnnotationCntPerScene('scene2', 2);
1544+
1545+
return Plotly.deleteTraces(gd, [1]);
1546+
})
1547+
.then(function() {
1548+
assertAnnotationCntPerScene('scene', 1);
1549+
assertAnnotationCntPerScene('scene2', 0);
1550+
1551+
return Plotly.deleteTraces(gd, [0]);
1552+
})
1553+
.then(function() {
1554+
assertAnnotationCntPerScene('scene', 0);
1555+
assertAnnotationCntPerScene('scene2', 0);
1556+
})
1557+
.catch(fail)
1558+
.then(done);
1559+
});
1560+
1561+
it('should contribute to scene axis autorange', function(done) {
1562+
function assertSceneAxisRanges(xRange, yRange, zRange) {
1563+
var sceneLayout = gd._fullLayout.scene;
1564+
1565+
expect(sceneLayout.xaxis.range).toBeCloseToArray(xRange, 1, 'xaxis range');
1566+
expect(sceneLayout.yaxis.range).toBeCloseToArray(yRange, 1, 'yaxis range');
1567+
expect(sceneLayout.zaxis.range).toBeCloseToArray(zRange, 1, 'zaxis range');
1568+
}
1569+
1570+
Plotly.plot(gd, [{
1571+
type: 'scatter3d',
1572+
x: [1, 2, 3],
1573+
y: [1, 2, 3],
1574+
z: [1, 2, 1]
1575+
}], {
1576+
scene: {
1577+
annotations: [{
1578+
text: 'hello',
1579+
x: 1, y: 1, z: 3
1580+
}]
1581+
}
1582+
})
1583+
.then(function() {
1584+
assertSceneAxisRanges([0.9375, 3.0625], [0.9375, 3.0625], [0.9375, 3.0625]);
1585+
1586+
return Plotly.relayout(gd, 'scene.annotations[0].z', 10);
1587+
})
1588+
.then(function() {
1589+
assertSceneAxisRanges([0.9375, 3.0625], [0.9375, 3.0625], [0.7187, 10.2813]);
1590+
})
1591+
.catch(fail)
1592+
.then(done);
1593+
});
1594+
1595+
it('should allow text and tail position edits under `editable: true`', function(done) {
1596+
function editText(newText, expectation) {
1597+
return new Promise(function(resolve) {
1598+
gd.once('plotly_relayout', function(eventData) {
1599+
expect(eventData).toEqual(expectation);
1600+
setTimeout(resolve, 0);
1601+
});
1602+
1603+
var clickNode = d3.select('g.annotation-text-g').select('g').node();
1604+
clickNode.dispatchEvent(new window.MouseEvent('click'));
1605+
1606+
var editNode = d3.select('.plugin-editable.editable').node();
1607+
editNode.dispatchEvent(new window.FocusEvent('focus'));
1608+
1609+
editNode.textContent = newText;
1610+
editNode.dispatchEvent(new window.FocusEvent('focus'));
1611+
editNode.dispatchEvent(new window.FocusEvent('blur'));
1612+
});
1613+
}
1614+
1615+
function moveArrowTail(dx, dy, expectation) {
1616+
var px = 243;
1617+
var py = 150;
1618+
1619+
return new Promise(function(resolve) {
1620+
gd.once('plotly_relayout', function(eventData) {
1621+
expect(eventData).toEqual(expectation);
1622+
resolve();
1623+
});
1624+
1625+
mouseEvent('mousemove', px, py);
1626+
mouseEvent('mousedown', px, py);
1627+
mouseEvent('mousemove', px + dx, py + dy);
1628+
mouseEvent('mouseup', px + dx, py + dy);
1629+
});
1630+
}
1631+
1632+
Plotly.plot(gd, [{
1633+
type: 'scatter3d',
1634+
x: [1, 2, 3],
1635+
y: [1, 2, 3],
1636+
z: [1, 2, 1]
1637+
}], {
1638+
scene: {
1639+
annotations: [{
1640+
text: 'hello',
1641+
x: 2, y: 2, z: 2,
1642+
font: { size: 30 }
1643+
}]
1644+
},
1645+
margin: {l: 0, t: 0, r: 0, b: 0},
1646+
width: 500,
1647+
height: 500
1648+
}, {
1649+
editable: true
1650+
})
1651+
.then(function() {
1652+
return editText('allo', {'scene.annotations[0].text': 'allo'});
1653+
})
1654+
.then(function() {
1655+
return moveArrowTail(-100, -50, {
1656+
'scene.annotations[0].ax': -110,
1657+
'scene.annotations[0].ay': -80
1658+
});
1659+
})
1660+
.catch(fail)
1661+
.then(done);
1662+
});
1663+
});

0 commit comments

Comments
 (0)