From eba8bd7590d3b422577ad051aecf73be634b3972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Thu, 18 Aug 2016 12:19:29 -0400 Subject: [PATCH 01/11] first pass js jupyter tests --- .../tests/test_core/test_jupyter/.gitignore | 2 + .../test_core/test_jupyter/lib/server.js | 93 +++++++++++++++++++ .../test_jupyter/lib/tape-wrapper.js | 22 +++++ .../tests/test_core/test_jupyter/package.json | 19 ++++ plotly/tests/test_core/test_jupyter/test.js | 15 +++ .../test_core/test_jupyter/test_jupyter.py | 46 +++++++++ 6 files changed, 197 insertions(+) create mode 100644 plotly/tests/test_core/test_jupyter/.gitignore create mode 100644 plotly/tests/test_core/test_jupyter/lib/server.js create mode 100644 plotly/tests/test_core/test_jupyter/lib/tape-wrapper.js create mode 100644 plotly/tests/test_core/test_jupyter/package.json create mode 100644 plotly/tests/test_core/test_jupyter/test.js create mode 100644 plotly/tests/test_core/test_jupyter/test_jupyter.py diff --git a/plotly/tests/test_core/test_jupyter/.gitignore b/plotly/tests/test_core/test_jupyter/.gitignore new file mode 100644 index 00000000000..44e342629a0 --- /dev/null +++ b/plotly/tests/test_core/test_jupyter/.gitignore @@ -0,0 +1,2 @@ +node_modules +fixtures/*.html diff --git a/plotly/tests/test_core/test_jupyter/lib/server.js b/plotly/tests/test_core/test_jupyter/lib/server.js new file mode 100644 index 00000000000..6929a24cdb1 --- /dev/null +++ b/plotly/tests/test_core/test_jupyter/lib/server.js @@ -0,0 +1,93 @@ +var http = require('http'); +var url = require('url'); +var fs = require('fs'); +var path = require('path'); +var ecstatic = require('ecstatic'); +var browserify = require('browserify'); +var cheerio = require('cheerio'); +var chrome = require('chrome-launch'); +var tapParser = require('tap-parser'); + +var PORT = 8080; +var PATH_ROOT = path.join(__dirname, '..'); +var PATH_FIXTURES = path.join(PATH_ROOT, 'fixtures'); +var PATH_INDEX = path.join(PATH_FIXTURES, 'test.html'); +var PATH_INDEX_STUB = path.join(PATH_FIXTURES, 'test.tmp.html'); +var PATH_TEST_FILE = path.join(PATH_ROOT, 'test.js'); +var PATH_TEST_BUNDLE = path.join(PATH_ROOT, 'test.tmp.js'); +var URL = 'http://localhost:' + PORT + '/fixtures/test.tmp.html'; +var EXIT_CODE = 0; + +// main +stubIndex() + .then(bundleTests) + .then(startServer) + .then(launch) + +function stubIndex() { + return new Promise(function(resolve, reject) { + var html = fs.readFileSync(PATH_INDEX, 'utf-8'); + var $ = cheerio.load(html); + + $('body').append(''); + + fs.writeFile(PATH_INDEX_STUB, $.html(), resolve); + }); +} + +function bundleTests() { + return new Promise(function(resolve, reject) { + var wsBundle = fs.createWriteStream(PATH_TEST_BUNDLE); + + browserify(PATH_TEST_FILE, { debug: true }) + .bundle() + .pipe(wsBundle); + + wsBundle.on('close', resolve); + }); +} + +function startServer() { + return new Promise(function(resolve, reject) { + var server = http.createServer(ecstatic({ root: PATH_ROOT })); + + server.on('request', handle); + + server.listen(PORT, resolve); + }); +} + +function handle(req, res) { + var query = url.parse(req.url).query || ''; + var parser = tapParser(); + + function is(query, root) { + return query.indexOf(root) !== -1; + } + + if(is(query, 'data')) handleData(req, res); + if(is(query, 'done')) handleDone(); + + function handleData(req, res) { + req.pipe(parser); + req.pipe(process.stdout); + } + + parser.on('assert', function(assert) { + if(EXIT_CODE === 0 && assert.ok === false) EXIT_CODE = 1; + }) + + function handleDone() { + removeBuildFiles(); + process.exit(EXIT_CODE); + } +} + +function launch() { + chrome(URL); +} + +function removeBuildFiles() { + fs.unlinkSync(PATH_INDEX_STUB); + fs.unlinkSync(PATH_TEST_BUNDLE); +} diff --git a/plotly/tests/test_core/test_jupyter/lib/tape-wrapper.js b/plotly/tests/test_core/test_jupyter/lib/tape-wrapper.js new file mode 100644 index 00000000000..47d8a59124f --- /dev/null +++ b/plotly/tests/test_core/test_jupyter/lib/tape-wrapper.js @@ -0,0 +1,22 @@ +var test = require('tape'); +var xhr = require('xhr'); + +var cnt = 0; +var noop = function() {}; + +var post = function(query, data) { + var opts = data ? { body: data } : {}; + xhr.post('/?' + query + '&' + (cnt++), opts, noop); +}; + +var ws = test.createStream(); + +ws.on('data', function(data) { + post('data', data) +}); + +test.onFinish(function() { + post('done'); +}); + +module.exports = test; diff --git a/plotly/tests/test_core/test_jupyter/package.json b/plotly/tests/test_core/test_jupyter/package.json new file mode 100644 index 00000000000..6bec0caab31 --- /dev/null +++ b/plotly/tests/test_core/test_jupyter/package.json @@ -0,0 +1,19 @@ +{ + "name": "plotly.py-jupyter-tests", + "version": "1.0.0", + "description": "js deps for plotly.py jupyter tests", + "scripts": { + "test": "node lib/server.js" + }, + "author": "Plotly Inc.", + "license": "MIT", + "dependencies": { + "browserify": "^13.1.0", + "cheerio": "^0.20.0", + "chrome-launch": "^1.1.4", + "ecstatic": "^2.1.0", + "tap-parser": "^2.0.0", + "tape": "^4.6.0", + "xhr": "^2.2.2" + } +} diff --git a/plotly/tests/test_core/test_jupyter/test.js b/plotly/tests/test_core/test_jupyter/test.js new file mode 100644 index 00000000000..3b6b521c0f5 --- /dev/null +++ b/plotly/tests/test_core/test_jupyter/test.js @@ -0,0 +1,15 @@ +var test = require('./lib/tape-wrapper'); + +test('should have the correct number of script tags', function(t) { + t.plan(1); + + var nodes = document.querySelectorAll('script'); + t.equal(nodes.length, 8); +}); + +test('should have one plotly.js graph', function(t) { + t.plan(1); + + var nodes = document.querySelectorAll('.js-plotly-plot'); + t.equal(nodes.length, 1); +}); diff --git a/plotly/tests/test_core/test_jupyter/test_jupyter.py b/plotly/tests/test_core/test_jupyter/test_jupyter.py new file mode 100644 index 00000000000..5bcfe367816 --- /dev/null +++ b/plotly/tests/test_core/test_jupyter/test_jupyter.py @@ -0,0 +1,46 @@ +""" +test__jupyter + +""" +from __future__ import absolute_import + +import nbformat +from nbconvert import HTMLExporter +from nbconvert.preprocessors import ExecutePreprocessor + +from unittest import TestCase +from os import path +import subprocess + +import plotly + +PATH_ROOT = path.dirname(__file__) +PATH_FIXTURES = path.join(PATH_ROOT, 'fixtures') +PATH_TEST_NB = path.join(PATH_FIXTURES, 'test.ipynb') +PATH_TEST_HTML = path.join(PATH_FIXTURES, 'test.html') + + +class PlotlyJupyterTestCase(TestCase): + def setUp(self): + with open(PATH_TEST_NB, 'r') as f: + self.nb = nbformat.read(f, as_version=4) + + self.ep = ExecutePreprocessor(timeout=600) + self.html_exporter = HTMLExporter() + + self.ep.preprocess(self.nb, {'metadata': {'path': '.'}}) + (self.body, _) = self.html_exporter.from_notebook_node(self.nb) + + with open(PATH_TEST_HTML, 'w') as f: + f.write(self.body) + + def test_one(self): + proc = subprocess.Popen(['npm', 'test'], + cwd=PATH_ROOT, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + (_, stderr) = proc.communicate() + + if stderr: + self.fail('One or more javascript test failed') From 673d0a30bed532850a75114149d35215d312399a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Thu, 18 Aug 2016 12:28:32 -0400 Subject: [PATCH 02/11] un-ignore fixture ipynb files --- .../tests/test_core/test_jupyter/.gitignore | 1 + .../test_jupyter/fixtures/test.ipynb | 57 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 plotly/tests/test_core/test_jupyter/fixtures/test.ipynb diff --git a/plotly/tests/test_core/test_jupyter/.gitignore b/plotly/tests/test_core/test_jupyter/.gitignore index 44e342629a0..226eaffa30d 100644 --- a/plotly/tests/test_core/test_jupyter/.gitignore +++ b/plotly/tests/test_core/test_jupyter/.gitignore @@ -1,2 +1,3 @@ node_modules fixtures/*.html +!fixtures/*.ipynb diff --git a/plotly/tests/test_core/test_jupyter/fixtures/test.ipynb b/plotly/tests/test_core/test_jupyter/fixtures/test.ipynb new file mode 100644 index 00000000000..4fe8216a85f --- /dev/null +++ b/plotly/tests/test_core/test_jupyter/fixtures/test.ipynb @@ -0,0 +1,57 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from plotly.offline import plot, iplot, init_notebook_mode\n", + "import plotly.graph_objs as go\n", + "\n", + "# Make plotly work with Jupyter notebook\n", + "init_notebook_mode()\n", + "\n", + "keys=['one','two','three']\n", + "values=[1,2,3]\n", + "\n", + "iplot({\n", + " \"data\": [go.Bar(x=keys, y=values)],\n", + " \"layout\": go.Layout(title=\"Sample Bar Chart\")\n", + "})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.2" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} From f02b6a7b03b0dd1c61233d734e72481fee9f3a1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Thu, 25 Aug 2016 11:25:39 -0400 Subject: [PATCH 03/11] standardize optional requirements for CircleCI - add coverage to optional requirements - pip install optional requirements and no need for nothing else --- optional-requirements.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/optional-requirements.txt b/optional-requirements.txt index 22342b1be71..5d2be623c55 100644 --- a/optional-requirements.txt +++ b/optional-requirements.txt @@ -12,10 +12,11 @@ numpy # matplotlib==1.3.1 ## testing dependencies ## -nose==1.3.3 +nose +coverage -## ipython dependencies ## -ipython[all]==3.0.0 +## ipython ## +ipython ## pandas deps for some matplotlib functionality ## pandas From cedb7f1733bed2661b1c4149dfc6c1c1ed408105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Tue, 27 Sep 2016 10:42:46 -0400 Subject: [PATCH 04/11] add requirements for jupyter test on circleci --- circle.yml | 7 +++++++ optional-requirements.txt | 3 +++ 2 files changed, 10 insertions(+) diff --git a/circle.yml b/circle.yml index 30ae291017a..38a4071b6fa 100644 --- a/circle.yml +++ b/circle.yml @@ -8,6 +8,10 @@ machine: PLOTLY_TOX_PYTHON_33: /home/ubuntu/.pyenv/versions/3.3.3/bin/python3.3 PLOTLY_TOX_PYTHON_34: /home/ubuntu/.pyenv/versions/3.4.3/bin/python3.4 PLOTLY_TOX_PYTHON_35: /home/ubuntu/.pyenv/versions/3.5.0/bin/python3.5 + PLOTLY_JUPYTER_TEST_DIR: ${PLOTLY_PACKAGE_ROOT}/plotly/tests/test_core/test_jupyter + + node: + version: 4.2.1 dependencies: @@ -21,6 +25,9 @@ dependencies: # we need to cd out of the project root to ensure the install worked - cd ~ && python -c "import plotly" + # install jupyter test JS requirements + - cd ${PLOTLY_JUPYTER_TEST_DIR} && npm i + cache_directories: # cache everything that tox installs for us. diff --git a/optional-requirements.txt b/optional-requirements.txt index 5d2be623c55..9dcc04023b1 100644 --- a/optional-requirements.txt +++ b/optional-requirements.txt @@ -24,3 +24,6 @@ pandas ## scipy deps for some FigureFactory functions ## scipy +## jupyter ## +jupyter +ipykernel From 590f485e1d5a41ea508e7afd4f0910d64a32eb29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Tue, 27 Sep 2016 10:48:37 -0400 Subject: [PATCH 05/11] improve jupyter test design: - make lib/server.js handle arbitrary html index and js test file (to test multiple ipynb fixutres) - use ipykernel to determine which python version nosetest is launched w/ - use domready to make sure that tests are ran after DOM is ready --- .../test_core/test_jupyter/lib/server.js | 54 +++++++++++++++---- .../test_jupyter/lib/tape-wrapper.js | 11 ++++ .../tests/test_core/test_jupyter/package.json | 1 + .../test_core/test_jupyter/test_jupyter.py | 43 ++++++++++----- 4 files changed, 86 insertions(+), 23 deletions(-) diff --git a/plotly/tests/test_core/test_jupyter/lib/server.js b/plotly/tests/test_core/test_jupyter/lib/server.js index 6929a24cdb1..f9464ad0afe 100644 --- a/plotly/tests/test_core/test_jupyter/lib/server.js +++ b/plotly/tests/test_core/test_jupyter/lib/server.js @@ -2,27 +2,48 @@ var http = require('http'); var url = require('url'); var fs = require('fs'); var path = require('path'); + var ecstatic = require('ecstatic'); var browserify = require('browserify'); var cheerio = require('cheerio'); -var chrome = require('chrome-launch'); var tapParser = require('tap-parser'); +var chrome = require('chrome-launch'); var PORT = 8080; var PATH_ROOT = path.join(__dirname, '..'); -var PATH_FIXTURES = path.join(PATH_ROOT, 'fixtures'); -var PATH_INDEX = path.join(PATH_FIXTURES, 'test.html'); -var PATH_INDEX_STUB = path.join(PATH_FIXTURES, 'test.tmp.html'); -var PATH_TEST_FILE = path.join(PATH_ROOT, 'test.js'); +var PATH_INDEX_STUB = path.join(PATH_ROOT, 'index.tmp.html'); var PATH_TEST_BUNDLE = path.join(PATH_ROOT, 'test.tmp.js'); -var URL = 'http://localhost:' + PORT + '/fixtures/test.tmp.html'; + +var URL = 'http://localhost:' + PORT + '/index.tmp.html'; var EXIT_CODE = 0; -// main -stubIndex() - .then(bundleTests) - .then(startServer) - .then(launch) +if(process.argv.length !== 4) { + throw new Error('must provide path to html and js files'); +} + +var PATH_INDEX = process.argv[2]; +var PATH_TEST_FILE = process.argv[3]; + +main(); + +function main() { + scanInput(); + + stubIndex() + .then(bundleTests) + .then(startServer) + .then(launch); +} + +function scanInput() { + var reqFiles = [PATH_INDEX, PATH_TEST_FILE]; + + reqFiles.forEach(function(filePath) { + if(!doesFileExist(filePath)) { + throw new Error(filePath + ' does not exist'); + } + }); +} function stubIndex() { return new Promise(function(resolve, reject) { @@ -91,3 +112,14 @@ function removeBuildFiles() { fs.unlinkSync(PATH_INDEX_STUB); fs.unlinkSync(PATH_TEST_BUNDLE); } + +function doesFileExist(filePath) { + try { + if(fs.statSync(filePath).isFile()) return true; + } + catch(e) { + return false; + } + + return false; +} diff --git a/plotly/tests/test_core/test_jupyter/lib/tape-wrapper.js b/plotly/tests/test_core/test_jupyter/lib/tape-wrapper.js index 47d8a59124f..88759ddfc78 100644 --- a/plotly/tests/test_core/test_jupyter/lib/tape-wrapper.js +++ b/plotly/tests/test_core/test_jupyter/lib/tape-wrapper.js @@ -1,5 +1,8 @@ +'use strict'; + var test = require('tape'); var xhr = require('xhr'); +var domready = require('domready'); var cnt = 0; var noop = function() {}; @@ -19,4 +22,12 @@ test.onFinish(function() { post('done'); }); +test('should not crash browser', function(t) { + t.plan(1); + + domready(function() { + t.pass('domready'); + }); +}); + module.exports = test; diff --git a/plotly/tests/test_core/test_jupyter/package.json b/plotly/tests/test_core/test_jupyter/package.json index 6bec0caab31..c99e7062d7a 100644 --- a/plotly/tests/test_core/test_jupyter/package.json +++ b/plotly/tests/test_core/test_jupyter/package.json @@ -11,6 +11,7 @@ "browserify": "^13.1.0", "cheerio": "^0.20.0", "chrome-launch": "^1.1.4", + "domready": "^1.0.8", "ecstatic": "^2.1.0", "tap-parser": "^2.0.0", "tape": "^4.6.0", diff --git a/plotly/tests/test_core/test_jupyter/test_jupyter.py b/plotly/tests/test_core/test_jupyter/test_jupyter.py index 5bcfe367816..b42a04739b0 100644 --- a/plotly/tests/test_core/test_jupyter/test_jupyter.py +++ b/plotly/tests/test_core/test_jupyter/test_jupyter.py @@ -2,40 +2,49 @@ test__jupyter """ -from __future__ import absolute_import - import nbformat from nbconvert import HTMLExporter from nbconvert.preprocessors import ExecutePreprocessor +from ipykernel import kernelspec from unittest import TestCase from os import path import subprocess -import plotly - PATH_ROOT = path.dirname(__file__) PATH_FIXTURES = path.join(PATH_ROOT, 'fixtures') -PATH_TEST_NB = path.join(PATH_FIXTURES, 'test.ipynb') -PATH_TEST_HTML = path.join(PATH_FIXTURES, 'test.html') +PATH_JS_TESTS = path.join(PATH_ROOT, 'js_tests') + +class Common(TestCase): + __test__ = False + name = None -class PlotlyJupyterTestCase(TestCase): def setUp(self): - with open(PATH_TEST_NB, 'r') as f: + self.path_test_nb = path.join(PATH_FIXTURES, self.name + '.ipynb') + self.path_test_html = path.join(PATH_FIXTURES, self.name + '.html') + self.path_test_js = path.join(PATH_JS_TESTS, self.name + '.js') + + self.kernel_name = kernelspec.KERNEL_NAME + + with open(self.path_test_nb, 'r') as f: self.nb = nbformat.read(f, as_version=4) - self.ep = ExecutePreprocessor(timeout=600) + self.ep = ExecutePreprocessor(timeout=600, + kernel_name=self.kernel_name) + self.html_exporter = HTMLExporter() self.ep.preprocess(self.nb, {'metadata': {'path': '.'}}) (self.body, _) = self.html_exporter.from_notebook_node(self.nb) - with open(PATH_TEST_HTML, 'w') as f: + with open(self.path_test_html, 'w') as f: f.write(self.body) - def test_one(self): - proc = subprocess.Popen(['npm', 'test'], + def test_js(self): + cmd = ['npm', 'test', '--', self.path_test_html, self.path_test_js] + + proc = subprocess.Popen(cmd, cwd=PATH_ROOT, stdout=subprocess.PIPE, stderr=subprocess.PIPE) @@ -44,3 +53,13 @@ def test_one(self): if stderr: self.fail('One or more javascript test failed') + + +class PlotlyJupyterConnectedFalseTestCase(Common): + __test__ = True + name = 'connected_false' + + +class PlotlyJupyterConnectedTrueTestCase(Common): + __test__ = True + name = 'connected_true' From 289204901d79bf7904fda2584c7908be262ae972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Tue, 27 Sep 2016 10:50:07 -0400 Subject: [PATCH 06/11] split ipynb fixture and js test file into two cases: - one testing offline connected=True - one testing offline connected=False (the default) --- .../{test.ipynb => connected_false.ipynb} | 0 .../fixtures/connected_true.ipynb | 57 +++++++++++++++++++ .../test_jupyter/js_tests/connected_false.js | 31 ++++++++++ .../test_jupyter/js_tests/connected_true.js | 31 ++++++++++ plotly/tests/test_core/test_jupyter/test.js | 15 ----- 5 files changed, 119 insertions(+), 15 deletions(-) rename plotly/tests/test_core/test_jupyter/fixtures/{test.ipynb => connected_false.ipynb} (100%) create mode 100644 plotly/tests/test_core/test_jupyter/fixtures/connected_true.ipynb create mode 100644 plotly/tests/test_core/test_jupyter/js_tests/connected_false.js create mode 100644 plotly/tests/test_core/test_jupyter/js_tests/connected_true.js delete mode 100644 plotly/tests/test_core/test_jupyter/test.js diff --git a/plotly/tests/test_core/test_jupyter/fixtures/test.ipynb b/plotly/tests/test_core/test_jupyter/fixtures/connected_false.ipynb similarity index 100% rename from plotly/tests/test_core/test_jupyter/fixtures/test.ipynb rename to plotly/tests/test_core/test_jupyter/fixtures/connected_false.ipynb diff --git a/plotly/tests/test_core/test_jupyter/fixtures/connected_true.ipynb b/plotly/tests/test_core/test_jupyter/fixtures/connected_true.ipynb new file mode 100644 index 00000000000..74eb39fc850 --- /dev/null +++ b/plotly/tests/test_core/test_jupyter/fixtures/connected_true.ipynb @@ -0,0 +1,57 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from plotly.offline import plot, iplot, init_notebook_mode\n", + "import plotly.graph_objs as go\n", + "\n", + "# Make plotly work with Jupyter notebook\n", + "init_notebook_mode(connected=True)\n", + "\n", + "keys=['one','two','three']\n", + "values=[1,2,3]\n", + "\n", + "iplot({\n", + " \"data\": [go.Bar(x=keys, y=values)],\n", + " \"layout\": go.Layout(title=\"Sample Bar Chart\")\n", + "})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.2" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/plotly/tests/test_core/test_jupyter/js_tests/connected_false.js b/plotly/tests/test_core/test_jupyter/js_tests/connected_false.js new file mode 100644 index 00000000000..bcb500aa769 --- /dev/null +++ b/plotly/tests/test_core/test_jupyter/js_tests/connected_false.js @@ -0,0 +1,31 @@ +'use strict'; + +var test = require('../lib/tape-wrapper'); + +test('should load plotly.js', function(t) { + t.plan(1); + + window.require(['plotly'], function(Plotly) { + t.equal(typeof Plotly, 'object'); + }); +}); + +test('should have one plotly.js graph', function(t) { + t.plan(1); + + var nodes = document.querySelectorAll('.js-plotly-plot'); + t.equal(nodes.length, 1); +}); + +test('should inject raw plotly.js code into DOM', function(t) { + t.plan(1); + + var nodes = document.querySelectorAll('script'); + nodes = Array.prototype.slice.call(nodes, 0, 10); + + var results = nodes.filter(function(node) { + return node.innerHTML.substr(0, 19) === 'if(!window.Plotly){'; + }); + + t.equal(results.length, 1); +}); diff --git a/plotly/tests/test_core/test_jupyter/js_tests/connected_true.js b/plotly/tests/test_core/test_jupyter/js_tests/connected_true.js new file mode 100644 index 00000000000..8ec2cc02df7 --- /dev/null +++ b/plotly/tests/test_core/test_jupyter/js_tests/connected_true.js @@ -0,0 +1,31 @@ +'use strict'; + +var test = require('../lib/tape-wrapper'); + +test('should load plotly.js', function(t) { + t.plan(1); + + window.require(['plotly'], function(Plotly) { + t.equal(typeof Plotly, 'object'); + }); +}); + +test('should have one plotly.js graph', function(t) { + t.plan(1); + + var nodes = document.querySelectorAll('.js-plotly-plot'); + t.equal(nodes.length, 1); +}); + +test('should link to plotly.js CDN', function(t) { + t.plan(1); + + var nodes = document.querySelectorAll('script'); + nodes = Array.prototype.slice.call(nodes, 0); + + var results = nodes.filter(function(node) { + return node.src === 'https://cdn.plot.ly/plotly-latest.min.js'; + }); + + t.equal(results.length, 1); +}); diff --git a/plotly/tests/test_core/test_jupyter/test.js b/plotly/tests/test_core/test_jupyter/test.js deleted file mode 100644 index 3b6b521c0f5..00000000000 --- a/plotly/tests/test_core/test_jupyter/test.js +++ /dev/null @@ -1,15 +0,0 @@ -var test = require('./lib/tape-wrapper'); - -test('should have the correct number of script tags', function(t) { - t.plan(1); - - var nodes = document.querySelectorAll('script'); - t.equal(nodes.length, 8); -}); - -test('should have one plotly.js graph', function(t) { - t.plan(1); - - var nodes = document.querySelectorAll('.js-plotly-plot'); - t.equal(nodes.length, 1); -}); From 27966e5c65506aaab7e5c4b8c8e459fde5d8355d Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Thu, 5 Jan 2017 10:12:39 -0800 Subject: [PATCH 07/11] Move `test_jupyter` into *optional* test cases. --- plotly/tests/{test_core => test_optional}/test_jupyter/.gitignore | 0 .../test_jupyter/fixtures/connected_false.ipynb | 0 .../test_jupyter/fixtures/connected_true.ipynb | 0 .../test_jupyter/js_tests/connected_false.js | 0 .../test_jupyter/js_tests/connected_true.js | 0 .../tests/{test_core => test_optional}/test_jupyter/lib/server.js | 0 .../{test_core => test_optional}/test_jupyter/lib/tape-wrapper.js | 0 .../tests/{test_core => test_optional}/test_jupyter/package.json | 0 .../{test_core => test_optional}/test_jupyter/test_jupyter.py | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename plotly/tests/{test_core => test_optional}/test_jupyter/.gitignore (100%) rename plotly/tests/{test_core => test_optional}/test_jupyter/fixtures/connected_false.ipynb (100%) rename plotly/tests/{test_core => test_optional}/test_jupyter/fixtures/connected_true.ipynb (100%) rename plotly/tests/{test_core => test_optional}/test_jupyter/js_tests/connected_false.js (100%) rename plotly/tests/{test_core => test_optional}/test_jupyter/js_tests/connected_true.js (100%) rename plotly/tests/{test_core => test_optional}/test_jupyter/lib/server.js (100%) rename plotly/tests/{test_core => test_optional}/test_jupyter/lib/tape-wrapper.js (100%) rename plotly/tests/{test_core => test_optional}/test_jupyter/package.json (100%) rename plotly/tests/{test_core => test_optional}/test_jupyter/test_jupyter.py (100%) diff --git a/plotly/tests/test_core/test_jupyter/.gitignore b/plotly/tests/test_optional/test_jupyter/.gitignore similarity index 100% rename from plotly/tests/test_core/test_jupyter/.gitignore rename to plotly/tests/test_optional/test_jupyter/.gitignore diff --git a/plotly/tests/test_core/test_jupyter/fixtures/connected_false.ipynb b/plotly/tests/test_optional/test_jupyter/fixtures/connected_false.ipynb similarity index 100% rename from plotly/tests/test_core/test_jupyter/fixtures/connected_false.ipynb rename to plotly/tests/test_optional/test_jupyter/fixtures/connected_false.ipynb diff --git a/plotly/tests/test_core/test_jupyter/fixtures/connected_true.ipynb b/plotly/tests/test_optional/test_jupyter/fixtures/connected_true.ipynb similarity index 100% rename from plotly/tests/test_core/test_jupyter/fixtures/connected_true.ipynb rename to plotly/tests/test_optional/test_jupyter/fixtures/connected_true.ipynb diff --git a/plotly/tests/test_core/test_jupyter/js_tests/connected_false.js b/plotly/tests/test_optional/test_jupyter/js_tests/connected_false.js similarity index 100% rename from plotly/tests/test_core/test_jupyter/js_tests/connected_false.js rename to plotly/tests/test_optional/test_jupyter/js_tests/connected_false.js diff --git a/plotly/tests/test_core/test_jupyter/js_tests/connected_true.js b/plotly/tests/test_optional/test_jupyter/js_tests/connected_true.js similarity index 100% rename from plotly/tests/test_core/test_jupyter/js_tests/connected_true.js rename to plotly/tests/test_optional/test_jupyter/js_tests/connected_true.js diff --git a/plotly/tests/test_core/test_jupyter/lib/server.js b/plotly/tests/test_optional/test_jupyter/lib/server.js similarity index 100% rename from plotly/tests/test_core/test_jupyter/lib/server.js rename to plotly/tests/test_optional/test_jupyter/lib/server.js diff --git a/plotly/tests/test_core/test_jupyter/lib/tape-wrapper.js b/plotly/tests/test_optional/test_jupyter/lib/tape-wrapper.js similarity index 100% rename from plotly/tests/test_core/test_jupyter/lib/tape-wrapper.js rename to plotly/tests/test_optional/test_jupyter/lib/tape-wrapper.js diff --git a/plotly/tests/test_core/test_jupyter/package.json b/plotly/tests/test_optional/test_jupyter/package.json similarity index 100% rename from plotly/tests/test_core/test_jupyter/package.json rename to plotly/tests/test_optional/test_jupyter/package.json diff --git a/plotly/tests/test_core/test_jupyter/test_jupyter.py b/plotly/tests/test_optional/test_jupyter/test_jupyter.py similarity index 100% rename from plotly/tests/test_core/test_jupyter/test_jupyter.py rename to plotly/tests/test_optional/test_jupyter/test_jupyter.py From 28e2fe09d83dcb5a8439e41f1c157054476a334d Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Thu, 5 Jan 2017 11:00:14 -0800 Subject: [PATCH 08/11] Add `jupyter` to optional tox deps. --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index 312d0b5e36d..15ffaa0101f 100644 --- a/tox.ini +++ b/tox.ini @@ -58,6 +58,7 @@ deps= pytz==2016.10 optional: numpy==1.11.3 optional: ipython[all]==5.1.0 + optional: jupyter==1.0.0 optional: pandas==0.19.2 optional: scipy==0.18.1 From 8850febb27fbdbfaf4ed6cc64930c00fb9546f43 Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Thu, 5 Jan 2017 11:00:33 -0800 Subject: [PATCH 09/11] Cache jupyter node_modules deps. --- circle.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 38a4071b6fa..507d677eab9 100644 --- a/circle.yml +++ b/circle.yml @@ -8,7 +8,7 @@ machine: PLOTLY_TOX_PYTHON_33: /home/ubuntu/.pyenv/versions/3.3.3/bin/python3.3 PLOTLY_TOX_PYTHON_34: /home/ubuntu/.pyenv/versions/3.4.3/bin/python3.4 PLOTLY_TOX_PYTHON_35: /home/ubuntu/.pyenv/versions/3.5.0/bin/python3.5 - PLOTLY_JUPYTER_TEST_DIR: ${PLOTLY_PACKAGE_ROOT}/plotly/tests/test_core/test_jupyter + PLOTLY_JUPYTER_TEST_DIR: /home/ubuntu/${CIRCLE_PROJECT_REPONAME}/plotly/tests/test_optional/test_jupyter node: version: 4.2.1 @@ -32,6 +32,7 @@ dependencies: # cache everything that tox installs for us. - .tox + - ${PLOTLY_JUPYTER_TEST_DIR}/node_modules test: From 053cdd6c4f5900854df81e8caa1497d0eca6dd0f Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Thu, 5 Jan 2017 14:19:44 -0800 Subject: [PATCH 10/11] Quick fix for intermittent image server tests. Just wrapping them in a loop so that we can retry as needed for now. --- .../tests/test_core/test_image/test_image.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/plotly/tests/test_core/test_image/test_image.py b/plotly/tests/test_core/test_image/test_image.py index 7ccd298a845..409bb8cb48a 100644 --- a/plotly/tests/test_core/test_image/test_image.py +++ b/plotly/tests/test_core/test_image/test_image.py @@ -5,9 +5,11 @@ import tempfile import os import itertools +import warnings from nose.plugins.attrib import attr +from plotly import exceptions from plotly.plotly import plotly as py @@ -24,9 +26,20 @@ def setUp(self): def _generate_image_get_returns_valid_image_test(image_format, width, height, scale): def test(self): - image = py.image.get(self.data, image_format, width, height, scale) - if image_format in ['png', 'jpeg']: - assert imghdr.what('', image) == image_format + # TODO: better understand why this intermittently fails. See #649 + num_attempts = 5 + for i in range(num_attempts): + if i > 0: + warnings.warn('image test intermittently failed, retrying...') + try: + image = py.image.get(self.data, image_format, width, height, + scale) + if image_format in ['png', 'jpeg']: + assert imghdr.what('', image) == image_format + return + except (KeyError, exceptions.PlotlyError): + if i == num_attempts - 1: + raise return test From 7da7ed2979f0bbeb6165b5d14bc9649e64201df6 Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Thu, 5 Jan 2017 14:22:47 -0800 Subject: [PATCH 11/11] Use node a version that Circle has preinstalled. --- circle.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 507d677eab9..62a7bdb505a 100644 --- a/circle.yml +++ b/circle.yml @@ -11,7 +11,8 @@ machine: PLOTLY_JUPYTER_TEST_DIR: /home/ubuntu/${CIRCLE_PROJECT_REPONAME}/plotly/tests/test_optional/test_jupyter node: - version: 4.2.1 + # use a pre-installed version of node so we don't need to download it. + version: 4.2.2 dependencies: