Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 8fdd412

Browse filesBrowse files
authored
Merge pull request #549 from plotly/test-jupyter
Add JS tests for Jupyter NBs
2 parents 4191458 + 7da7ed2 commit 8fdd412
Copy full SHA for 8fdd412

File tree

Expand file treeCollapse file tree

13 files changed

+455
-6
lines changed
Filter options
Expand file treeCollapse file tree

13 files changed

+455
-6
lines changed

‎circle.yml

Copy file name to clipboardExpand all lines: circle.yml
+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ machine:
88
PLOTLY_TOX_PYTHON_33: /home/ubuntu/.pyenv/versions/3.3.3/bin/python3.3
99
PLOTLY_TOX_PYTHON_34: /home/ubuntu/.pyenv/versions/3.4.3/bin/python3.4
1010
PLOTLY_TOX_PYTHON_35: /home/ubuntu/.pyenv/versions/3.5.0/bin/python3.5
11+
PLOTLY_JUPYTER_TEST_DIR: /home/ubuntu/${CIRCLE_PROJECT_REPONAME}/plotly/tests/test_optional/test_jupyter
12+
13+
node:
14+
# use a pre-installed version of node so we don't need to download it.
15+
version: 4.2.2
1116

1217
dependencies:
1318

@@ -21,10 +26,14 @@ dependencies:
2126
# we need to cd out of the project root to ensure the install worked
2227
- cd ~ && python -c "import plotly"
2328

29+
# install jupyter test JS requirements
30+
- cd ${PLOTLY_JUPYTER_TEST_DIR} && npm i
31+
2432
cache_directories:
2533

2634
# cache everything that tox installs for us.
2735
- .tox
36+
- ${PLOTLY_JUPYTER_TEST_DIR}/node_modules
2837

2938
test:
3039

‎optional-requirements.txt

Copy file name to clipboardExpand all lines: optional-requirements.txt
+7-3Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,18 @@ numpy
1212
# matplotlib==1.3.1
1313

1414
## testing dependencies ##
15-
nose==1.3.3
15+
nose
16+
coverage
1617

17-
## ipython dependencies ##
18-
ipython[all]==3.0.0
18+
## ipython ##
19+
ipython
1920

2021
## pandas deps for some matplotlib functionality ##
2122
pandas
2223

2324
## scipy deps for some FigureFactory functions ##
2425
scipy
2526

27+
## jupyter ##
28+
jupyter
29+
ipykernel

‎plotly/tests/test_core/test_image/test_image.py

Copy file name to clipboardExpand all lines: plotly/tests/test_core/test_image/test_image.py
+16-3Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
import tempfile
66
import os
77
import itertools
8+
import warnings
89

910
from nose.plugins.attrib import attr
1011

12+
from plotly import exceptions
1113
from plotly.plotly import plotly as py
1214

1315

@@ -24,9 +26,20 @@ def setUp(self):
2426
def _generate_image_get_returns_valid_image_test(image_format,
2527
width, height, scale):
2628
def test(self):
27-
image = py.image.get(self.data, image_format, width, height, scale)
28-
if image_format in ['png', 'jpeg']:
29-
assert imghdr.what('', image) == image_format
29+
# TODO: better understand why this intermittently fails. See #649
30+
num_attempts = 5
31+
for i in range(num_attempts):
32+
if i > 0:
33+
warnings.warn('image test intermittently failed, retrying...')
34+
try:
35+
image = py.image.get(self.data, image_format, width, height,
36+
scale)
37+
if image_format in ['png', 'jpeg']:
38+
assert imghdr.what('', image) == image_format
39+
return
40+
except (KeyError, exceptions.PlotlyError):
41+
if i == num_attempts - 1:
42+
raise
3043

3144
return test
3245

+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
fixtures/*.html
3+
!fixtures/*.ipynb
+57Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"metadata": {
7+
"collapsed": false
8+
},
9+
"outputs": [],
10+
"source": [
11+
"from plotly.offline import plot, iplot, init_notebook_mode\n",
12+
"import plotly.graph_objs as go\n",
13+
"\n",
14+
"# Make plotly work with Jupyter notebook\n",
15+
"init_notebook_mode()\n",
16+
"\n",
17+
"keys=['one','two','three']\n",
18+
"values=[1,2,3]\n",
19+
"\n",
20+
"iplot({\n",
21+
" \"data\": [go.Bar(x=keys, y=values)],\n",
22+
" \"layout\": go.Layout(title=\"Sample Bar Chart\")\n",
23+
"})"
24+
]
25+
},
26+
{
27+
"cell_type": "code",
28+
"execution_count": null,
29+
"metadata": {
30+
"collapsed": true
31+
},
32+
"outputs": [],
33+
"source": []
34+
}
35+
],
36+
"metadata": {
37+
"kernelspec": {
38+
"display_name": "Python 3",
39+
"language": "python",
40+
"name": "python3"
41+
},
42+
"language_info": {
43+
"codemirror_mode": {
44+
"name": "ipython",
45+
"version": 3
46+
},
47+
"file_extension": ".py",
48+
"mimetype": "text/x-python",
49+
"name": "python",
50+
"nbconvert_exporter": "python",
51+
"pygments_lexer": "ipython3",
52+
"version": "3.5.2"
53+
}
54+
},
55+
"nbformat": 4,
56+
"nbformat_minor": 1
57+
}
+57Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"metadata": {
7+
"collapsed": false
8+
},
9+
"outputs": [],
10+
"source": [
11+
"from plotly.offline import plot, iplot, init_notebook_mode\n",
12+
"import plotly.graph_objs as go\n",
13+
"\n",
14+
"# Make plotly work with Jupyter notebook\n",
15+
"init_notebook_mode(connected=True)\n",
16+
"\n",
17+
"keys=['one','two','three']\n",
18+
"values=[1,2,3]\n",
19+
"\n",
20+
"iplot({\n",
21+
" \"data\": [go.Bar(x=keys, y=values)],\n",
22+
" \"layout\": go.Layout(title=\"Sample Bar Chart\")\n",
23+
"})"
24+
]
25+
},
26+
{
27+
"cell_type": "code",
28+
"execution_count": null,
29+
"metadata": {
30+
"collapsed": true
31+
},
32+
"outputs": [],
33+
"source": []
34+
}
35+
],
36+
"metadata": {
37+
"kernelspec": {
38+
"display_name": "Python 3",
39+
"language": "python",
40+
"name": "python3"
41+
},
42+
"language_info": {
43+
"codemirror_mode": {
44+
"name": "ipython",
45+
"version": 3
46+
},
47+
"file_extension": ".py",
48+
"mimetype": "text/x-python",
49+
"name": "python",
50+
"nbconvert_exporter": "python",
51+
"pygments_lexer": "ipython3",
52+
"version": "3.5.2"
53+
}
54+
},
55+
"nbformat": 4,
56+
"nbformat_minor": 1
57+
}
+31Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict';
2+
3+
var test = require('../lib/tape-wrapper');
4+
5+
test('should load plotly.js', function(t) {
6+
t.plan(1);
7+
8+
window.require(['plotly'], function(Plotly) {
9+
t.equal(typeof Plotly, 'object');
10+
});
11+
});
12+
13+
test('should have one plotly.js graph', function(t) {
14+
t.plan(1);
15+
16+
var nodes = document.querySelectorAll('.js-plotly-plot');
17+
t.equal(nodes.length, 1);
18+
});
19+
20+
test('should inject raw plotly.js code into DOM', function(t) {
21+
t.plan(1);
22+
23+
var nodes = document.querySelectorAll('script');
24+
nodes = Array.prototype.slice.call(nodes, 0, 10);
25+
26+
var results = nodes.filter(function(node) {
27+
return node.innerHTML.substr(0, 19) === 'if(!window.Plotly){';
28+
});
29+
30+
t.equal(results.length, 1);
31+
});
+31Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict';
2+
3+
var test = require('../lib/tape-wrapper');
4+
5+
test('should load plotly.js', function(t) {
6+
t.plan(1);
7+
8+
window.require(['plotly'], function(Plotly) {
9+
t.equal(typeof Plotly, 'object');
10+
});
11+
});
12+
13+
test('should have one plotly.js graph', function(t) {
14+
t.plan(1);
15+
16+
var nodes = document.querySelectorAll('.js-plotly-plot');
17+
t.equal(nodes.length, 1);
18+
});
19+
20+
test('should link to plotly.js CDN', function(t) {
21+
t.plan(1);
22+
23+
var nodes = document.querySelectorAll('script');
24+
nodes = Array.prototype.slice.call(nodes, 0);
25+
26+
var results = nodes.filter(function(node) {
27+
return node.src === 'https://cdn.plot.ly/plotly-latest.min.js';
28+
});
29+
30+
t.equal(results.length, 1);
31+
});
+125Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
var http = require('http');
2+
var url = require('url');
3+
var fs = require('fs');
4+
var path = require('path');
5+
6+
var ecstatic = require('ecstatic');
7+
var browserify = require('browserify');
8+
var cheerio = require('cheerio');
9+
var tapParser = require('tap-parser');
10+
var chrome = require('chrome-launch');
11+
12+
var PORT = 8080;
13+
var PATH_ROOT = path.join(__dirname, '..');
14+
var PATH_INDEX_STUB = path.join(PATH_ROOT, 'index.tmp.html');
15+
var PATH_TEST_BUNDLE = path.join(PATH_ROOT, 'test.tmp.js');
16+
17+
var URL = 'http://localhost:' + PORT + '/index.tmp.html';
18+
var EXIT_CODE = 0;
19+
20+
if(process.argv.length !== 4) {
21+
throw new Error('must provide path to html and js files');
22+
}
23+
24+
var PATH_INDEX = process.argv[2];
25+
var PATH_TEST_FILE = process.argv[3];
26+
27+
main();
28+
29+
function main() {
30+
scanInput();
31+
32+
stubIndex()
33+
.then(bundleTests)
34+
.then(startServer)
35+
.then(launch);
36+
}
37+
38+
function scanInput() {
39+
var reqFiles = [PATH_INDEX, PATH_TEST_FILE];
40+
41+
reqFiles.forEach(function(filePath) {
42+
if(!doesFileExist(filePath)) {
43+
throw new Error(filePath + ' does not exist');
44+
}
45+
});
46+
}
47+
48+
function stubIndex() {
49+
return new Promise(function(resolve, reject) {
50+
var html = fs.readFileSync(PATH_INDEX, 'utf-8');
51+
var $ = cheerio.load(html);
52+
53+
$('body').append('<script type="text/javascript" src="../test.tmp.js"></script>');
54+
55+
fs.writeFile(PATH_INDEX_STUB, $.html(), resolve);
56+
});
57+
}
58+
59+
function bundleTests() {
60+
return new Promise(function(resolve, reject) {
61+
var wsBundle = fs.createWriteStream(PATH_TEST_BUNDLE);
62+
63+
browserify(PATH_TEST_FILE, { debug: true })
64+
.bundle()
65+
.pipe(wsBundle);
66+
67+
wsBundle.on('close', resolve);
68+
});
69+
}
70+
71+
function startServer() {
72+
return new Promise(function(resolve, reject) {
73+
var server = http.createServer(ecstatic({ root: PATH_ROOT }));
74+
75+
server.on('request', handle);
76+
77+
server.listen(PORT, resolve);
78+
});
79+
}
80+
81+
function handle(req, res) {
82+
var query = url.parse(req.url).query || '';
83+
var parser = tapParser();
84+
85+
function is(query, root) {
86+
return query.indexOf(root) !== -1;
87+
}
88+
89+
if(is(query, 'data')) handleData(req, res);
90+
if(is(query, 'done')) handleDone();
91+
92+
function handleData(req, res) {
93+
req.pipe(parser);
94+
req.pipe(process.stdout);
95+
}
96+
97+
parser.on('assert', function(assert) {
98+
if(EXIT_CODE === 0 && assert.ok === false) EXIT_CODE = 1;
99+
})
100+
101+
function handleDone() {
102+
removeBuildFiles();
103+
process.exit(EXIT_CODE);
104+
}
105+
}
106+
107+
function launch() {
108+
chrome(URL);
109+
}
110+
111+
function removeBuildFiles() {
112+
fs.unlinkSync(PATH_INDEX_STUB);
113+
fs.unlinkSync(PATH_TEST_BUNDLE);
114+
}
115+
116+
function doesFileExist(filePath) {
117+
try {
118+
if(fs.statSync(filePath).isFile()) return true;
119+
}
120+
catch(e) {
121+
return false;
122+
}
123+
124+
return false;
125+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.