Skip to content

Commit f28cf05

Browse files
committed
CircleCI Tests (plotly#59)
* bump archetype version * install eslint globally, fix tabs * install requirements * clean up tests * unittest instead of implicit nosetest, all versions of python * rm 36 for now * just python 2 for now * typo * increase length of timeout * log the layout * remove duplicate visit
1 parent 87a0d73 commit f28cf05

File tree

9 files changed

+317
-79
lines changed

9 files changed

+317
-79
lines changed
Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,21 @@
11
machine:
2-
node:
3-
version: 4.4.7
2+
node:
3+
version: 6.9.2
4+
post:
5+
- pyenv global 2.7.10
6+
environment:
7+
TOX_PYTHON_27: python2.7
8+
9+
dependencies:
10+
pre:
11+
- npm install -g eslint
12+
- pip install tox
13+
- pip install -r dev-requirements.txt
14+
- npm install
15+
- node_modules/.bin/builder run build-dist
16+
- node_modules/.bin/builder run copy-lib
417

518
test:
6-
post:
7-
# Ensure steps leading up to publishing work.
8-
- node_modules/.bin/builder run build-dist
19+
override:
20+
- tox
21+
- npm run test
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
dash_html_components
2+
dash_renderer
3+
dash
4+
percy
5+
selenium
6+
mock
7+
tox
8+
tox-pyenv
9+
six
10+
plotly>=2.0.8
11+
requests[security]
12+
ipdb

packages/dash-core-components/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"license": "MIT",
2727
"dependencies": {
2828
"builder": "2.10.1",
29-
"dash-components-archetype": "^0.2.4",
29+
"dash-components-archetype": "^0.2.11",
3030
"moment": "^2.18.1",
3131
"radium": "^0.17.1",
3232
"ramda": "^0.23.0",
@@ -43,7 +43,7 @@
4343
},
4444
"devDependencies": {
4545
"component-playground": "^1.3.2",
46-
"dash-components-archetype-dev": "^0.2.4",
46+
"dash-components-archetype-dev": "^0.2.11",
4747
"enzyme": "^2.4.1"
4848
}
4949
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
from __future__ import absolute_import
2+
from selenium import webdriver
3+
from selenium.webdriver.common.keys import Keys
4+
import importlib
5+
import multiprocessing
6+
import percy
7+
import time
8+
import unittest
9+
10+
11+
class IntegrationTests(unittest.TestCase):
12+
13+
@classmethod
14+
def setUpClass(cls):
15+
super(IntegrationTests, cls).setUpClass()
16+
cls.driver = webdriver.Chrome()
17+
18+
loader = percy.ResourceLoader(webdriver=cls.driver)
19+
cls.percy_runner = percy.Runner(loader=loader)
20+
cls.percy_runner.initialize_build()
21+
22+
@classmethod
23+
def tearDownClass(cls):
24+
super(IntegrationTests, cls).tearDownClass()
25+
cls.driver.quit()
26+
cls.percy_runner.finalize_build()
27+
28+
def setUp(self):
29+
pass
30+
31+
def tearDown(self):
32+
time.sleep(3)
33+
self.server_process.terminate()
34+
time.sleep(3)
35+
36+
if hasattr(self, 'driver'):
37+
self.driver.quit()
38+
39+
40+
def startServer(self, app):
41+
def run():
42+
app.scripts.config.serve_locally = True
43+
app.run_server(
44+
port=8050,
45+
debug=False,
46+
processes=4
47+
)
48+
49+
# Run on a separate process so that it doesn't block
50+
self.server_process = multiprocessing.Process(target=run)
51+
self.server_process.start()
52+
time.sleep(0.5)
53+
54+
# Visit the dash page
55+
self.driver.get('http://localhost:8050')
56+
time.sleep(0.5)
57+
58+
# Inject an error and warning logger
59+
logger = '''
60+
window.tests = {};
61+
window.tests.console = {error: [], warn: [], log: []};
62+
63+
var _log = console.log;
64+
var _warn = console.warn;
65+
var _error = console.error;
66+
67+
console.log = function() {
68+
window.tests.console.log.push({method: 'log', arguments: arguments});
69+
return _log.apply(console, arguments);
70+
};
71+
72+
console.warn = function() {
73+
window.tests.console.warn.push({method: 'warn', arguments: arguments});
74+
return _warn.apply(console, arguments);
75+
};
76+
77+
console.error = function() {
78+
window.tests.console.error.push({method: 'error', arguments: arguments});
79+
return _error.apply(console, arguments);
80+
};
81+
'''
82+
self.driver.execute_script(logger)
Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +0,0 @@
1-
from os.path import dirname, basename, isfile
2-
import glob
3-
modules = glob.glob(dirname(__file__)+"/*.py")
4-
__all__ = [basename(f)[:-3] for f in modules if isfile(f)]
5-
Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +0,0 @@
1-
/* eslint-disable import/default */
2-
/* global require:false */
3-
import karmaRunner from 'dash-components-archetype-dev/karma-runner';
4-
5-
karmaRunner.setupEnvironment();
6-
7-
// Use webpack to infer and `require` tests automatically.
8-
var testsReq = require.context('../src', true, /\.test.js$/);
9-
testsReq.keys().map(testsReq);
10-
11-
karmaRunner.startKarma();
Lines changed: 103 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1-
import dash_core_components
2-
from dash.react import Dash
1+
# -*- coding: utf-8 -*-
2+
import dash_core_components as dcc
3+
import dash_html_components as html
4+
import dash
35
import importlib
46
import percy
57
from selenium import webdriver
68
from selenium.webdriver.common.keys import Keys
79
import time
810
import multiprocessing
911
import unittest
12+
import os
13+
14+
from .IntegrationTests import IntegrationTests
15+
from .utils import assert_clean_console, invincible, wait_for
1016

1117
# Download geckodriver: https://github.com/mozilla/geckodriver/releases
1218
# And add to path:
@@ -17,59 +23,100 @@
1723
# export PERCY_TOKEN=...
1824

1925

20-
class IntegrationTests(unittest.TestCase):
21-
22-
@classmethod
23-
def setUpClass(cls):
24-
super(IntegrationTests, cls).setUpClass()
25-
cls.driver = webdriver.Firefox()
26-
27-
loader = percy.ResourceLoader(
28-
webdriver=cls.driver
29-
)
30-
cls.percy_runner = percy.Runner(loader=loader)
31-
32-
cls.percy_runner.initialize_build()
33-
34-
@classmethod
35-
def tearDownClass(cls):
36-
super(IntegrationTests, cls).tearDownClass()
37-
cls.driver.quit()
38-
cls.percy_runner.finalize_build()
39-
40-
def setUp(s):
41-
pass
42-
43-
def tearDown(s):
44-
s.server_process.terminate()
45-
46-
def startServer(s, dash):
47-
def run():
48-
dash.run_server(
49-
port=8050,
50-
debug=False,
51-
component_suites=[
52-
'dash_core_components'
26+
class Tests(IntegrationTests):
27+
def setUp(self):
28+
self.driver = webdriver.Chrome()
29+
def wait_for_element_by_id(id):
30+
wait_for(lambda: None is not invincible(
31+
lambda: self.driver.find_element_by_id(id)
32+
))
33+
return self.driver.find_element_by_id(id)
34+
self.wait_for_element_by_id = wait_for_element_by_id
35+
36+
def wait_for_element_by_css_selector(css_selector):
37+
wait_for(lambda: None is not invincible(
38+
lambda: self.driver.find_element_by_css_selector(css_selector)
39+
))
40+
return self.driver.find_element_by_css_selector(css_selector)
41+
self.wait_for_element_by_css_selector = wait_for_element_by_css_selector
42+
43+
44+
def test_integration(self):
45+
app = dash.Dash(__name__)
46+
47+
app.layout = html.Div([
48+
html.Div(id='waitfor'),
49+
html.Label('Dropdown'),
50+
dcc.Dropdown(
51+
options=[
52+
{'label': 'New York City', 'value': 'NYC'},
53+
{'label': u'Montréal', 'value': 'MTL'},
54+
{'label': 'San Francisco', 'value': 'SF'}
55+
],
56+
value='MTL'
57+
),
58+
59+
html.Label('Multi-Select Dropdown'),
60+
dcc.Dropdown(
61+
options=[
62+
{'label': 'New York City', 'value': 'NYC'},
63+
{'label': u'Montréal', 'value': 'MTL'},
64+
{'label': 'San Francisco', 'value': 'SF'}
5365
],
54-
threaded=True
66+
value=['MTL', 'SF'],
67+
multi=True
68+
),
69+
70+
html.Label('Radio Items'),
71+
dcc.RadioItems(
72+
options=[
73+
{'label': 'New York City', 'value': 'NYC'},
74+
{'label': u'Montréal', 'value': 'MTL'},
75+
{'label': 'San Francisco', 'value': 'SF'}
76+
],
77+
value='MTL'
78+
),
79+
80+
html.Label('Checkboxes'),
81+
dcc.Checklist(
82+
options=[
83+
{'label': 'New York City', 'value': 'NYC'},
84+
{'label': u'Montréal', 'value': 'MTL'},
85+
{'label': 'San Francisco', 'value': 'SF'}
86+
],
87+
values=['MTL', 'SF']
88+
),
89+
90+
html.Label('Text Input'),
91+
dcc.Input(value='MTL', type='text'),
92+
93+
html.Label('Slider'),
94+
dcc.Slider(
95+
min=0,
96+
max=9,
97+
marks={i: 'Label {}'.format(i) if i == 1 else str(i) for i in range(1, 6)},
98+
value=5,
99+
),
100+
101+
html.Label('Graph'),
102+
dcc.Graph(
103+
id='graph',
104+
figure={
105+
'data': [{
106+
'x': [1, 2, 3],
107+
'y': [4, 1, 4]
108+
}]
109+
}
55110
)
56-
57-
# Run on a separate process so that it doesn't block
58-
s.server_process = multiprocessing.Process(target=run)
59-
s.server_process.start()
60-
time.sleep(0.5)
61-
62-
def test_integration(s):
63-
dash = Dash(__name__)
64-
65-
dash.layout = dash_core_components.Input(
66-
id='hello-world'
67-
)
68-
69-
s.startServer(dash)
70-
s.driver.get('http://localhost:8050')
71-
72-
el = s.driver.find_element_by_id('hello-world')
73-
74-
# Take a screenshot with percy
75-
s.percy_runner.snapshot(name='dash_core_components')
111+
])
112+
self.startServer(app)
113+
114+
try:
115+
self.wait_for_element_by_id('waitfor')
116+
except Exception as e:
117+
print(self.wait_for_element_by_id(
118+
'_dash-app-content').get_attribute('innerHTML'))
119+
raise e
120+
121+
if 'PERCY_PROJECT' in os.environ and 'PERCY_TOKEN' in os.environ:
122+
self.percy_runner.snapshot(name='dash_core_components')

0 commit comments

Comments
 (0)