From 105f5a4363b25d20f24b4425f99b47da0d2c395c Mon Sep 17 00:00:00 2001 From: Xing Han Lu Date: Mon, 4 Mar 2019 18:05:49 -0500 Subject: [PATCH 01/12] :snowflake: flake8 + pylint on usage apps --- demos/dash_reusable_components.py | 2 - demos/editor/callbacks.py | 15 +++++-- demos/editor/layout.py | 54 +++++++++++------------- demos/usage-animated-bfs.py | 2 +- demos/usage-breadthfirst-layout.py | 1 - demos/usage-circle-layout.py | 1 - demos/usage-compound-nodes.py | 1 - demos/usage-concentric-layout.py | 1 - demos/usage-concentric-social-network.py | 2 +- demos/usage-cose-bilkent-layout.py | 4 +- demos/usage-cose-layout.py | 1 - demos/usage-edge-types.py | 1 - demos/usage-grid-layout.py | 1 - demos/usage-grid-social-network.py | 2 +- demos/usage-initialisation.py | 1 - demos/usage-labels.py | 4 +- demos/usage-linkout-example.py | 1 - demos/usage-multiple-instances.py | 1 - demos/usage-phylogeny.py | 7 ++- demos/usage-pie-style.py | 1 - demos/usage-visual-style.py | 1 - usage-elements.py | 14 +++--- usage-events.py | 2 +- usage-stylesheet.py | 12 +++--- 24 files changed, 60 insertions(+), 72 deletions(-) diff --git a/demos/dash_reusable_components.py b/demos/dash_reusable_components.py index fd11fb5d..e998ddf4 100644 --- a/demos/dash_reusable_components.py +++ b/demos/dash_reusable_components.py @@ -1,5 +1,3 @@ -from textwrap import dedent - import dash_core_components as dcc import dash_html_components as html diff --git a/demos/editor/callbacks.py b/demos/editor/callbacks.py index dc06270f..17007b16 100644 --- a/demos/editor/callbacks.py +++ b/demos/editor/callbacks.py @@ -1,3 +1,4 @@ +# pylint: disable=W0612,R1705 import json from colour import Color @@ -28,7 +29,7 @@ def validate_positive(value): return min(0, value) -def validate_color(color, default='#999999'): +def validate_color(color: str, default='#999999'): ''' Check if a color is valid, if so returns the color, else return a default color :param color: The color to validate @@ -41,10 +42,19 @@ def validate_color(color, default='#999999'): try: # Converting 'deep sky blue' to 'deepskyblue' color = color.replace(" ", "") + + if color.startswith('rgb'): + values = color.replace('rgb(', '').replace(')', '').split(',') + + if len(values) == 3 and all(0 <= int(v) <= 255 for v in values): + return color + + return default + Color(color) # if everything goes fine then return True return color - except: # The color code was not found + except: # noqa return default @@ -225,7 +235,6 @@ def disable_side_endpoint_width(value): def disable_side_endpoint_height(value): return value != 'other' - # ############################## CYTOSCAPE ################################ @app.callback(Output('cytoscape', 'elements'), [Input('dropdown-select-element-list', 'value')]) diff --git a/demos/editor/layout.py b/demos/editor/layout.py index e440bf8d..4e512d00 100644 --- a/demos/editor/layout.py +++ b/demos/editor/layout.py @@ -437,7 +437,6 @@ id='dropdown-edge-curve-style', value='haystack', clearable=False, - searchable=False, options=drc.DropdownOptionsList( 'haystack', 'bezier', @@ -610,8 +609,7 @@ placeholder='Input value in % or px...', value='0px' ) - ]) for side in - ['source', 'target']], + ]) for side in ['source', 'target']], drc.NamedInput( name='Source Distance from node', @@ -637,8 +635,7 @@ drc.NamedRadioItems( name='Use Labels', id='radio-use-labels', - options=drc.DropdownOptionsList('yes', - 'no'), + options=drc.DropdownOptionsList('yes', 'no'), value='no' ), @@ -682,8 +679,7 @@ options=[{ 'label': element.capitalize(), 'value': f'div-label-{element}' - } for element in - LABEL_ELEMENT_TYPES], + } for element in LABEL_ELEMENT_TYPES], clearable=False, value='div-label-node' ), @@ -760,8 +756,7 @@ searchable=False, value='none' ) - ]) for element in - LABEL_ELEMENT_TYPES], + ]) for element in LABEL_ELEMENT_TYPES], ]), drc.NamedCard(title='Text Wrapping', size=4, children=[ drc.NamedDropdown( @@ -770,8 +765,7 @@ options=[{ 'label': element.capitalize(), 'value': f'div-text-wrapping-{element}' - } for element in - LABEL_ELEMENT_TYPES], + } for element in LABEL_ELEMENT_TYPES], clearable=False, value='div-text-wrapping-node' ), @@ -796,8 +790,7 @@ type='number', placeholder='Enter the maximum width in px...' ) - ]) for element in - LABEL_ELEMENT_TYPES], + ]) for element in LABEL_ELEMENT_TYPES], ]), drc.NamedCard(title='Label Alignment', size=4, children=[ drc.NamedRadioItems( @@ -848,23 +841,24 @@ value='div-text-margins-node' ), - *[html.Div(id=f'div-text-margins-{element}', - children=[ - drc.NamedInput( - name=f"{element.capitalize()} Margin X (px)", - id=f'input-{element}-text-margin-x', - type='number', - placeholder='Enter a value in px...' - ), - - drc.NamedInput( - name=f"{element.capitalize()} Margin Y(px)", - id=f'input-{element}-text-margin-y', - type='number', - placeholder='Enter a value in px...' - ) - ]) for element in - LABEL_ELEMENT_TYPES_ALL], + *[html.Div( + id=f'div-text-margins-{element}', + children=[ + drc.NamedInput( + name=f"{element.capitalize()} Margin X (px)", + id=f'input-{element}-text-margin-x', + type='number', + placeholder='Enter a value in px...' + ), + + drc.NamedInput( + name=f"{element.capitalize()} Margin Y(px)", + id=f'input-{element}-text-margin-y', + type='number', + placeholder='Enter a value in px...' + ) + ] + ) for element in LABEL_ELEMENT_TYPES_ALL], ]) ] ) diff --git a/demos/usage-animated-bfs.py b/demos/usage-animated-bfs.py index fda0c037..37ceb7c6 100644 --- a/demos/usage-animated-bfs.py +++ b/demos/usage-animated-bfs.py @@ -1,6 +1,6 @@ """ Original Demo: http://js.cytoscape.org/demos/animated-bfs/ -Original Code: https://github.com/cytoscape/cytoscape.js/tree/master/documentation/demos/animated-bfs +Code: https://github.com/cytoscape/cytoscape.js/tree/master/documentation/demos/animated-bfs Note: Animation Not Implemented yet, please refer to code. """ diff --git a/demos/usage-breadthfirst-layout.py b/demos/usage-breadthfirst-layout.py index 9c4cf4e3..5908090c 100644 --- a/demos/usage-breadthfirst-layout.py +++ b/demos/usage-breadthfirst-layout.py @@ -1,6 +1,5 @@ """ Original Demo: http://js.cytoscape.org/demos/images-breadthfirst-layout/ -Original Code: https://github.com/cytoscape/cytoscape.js/tree/master/documentation/demos/images-breadthfirst-layout Note: Click Animation is not implemented. """ diff --git a/demos/usage-circle-layout.py b/demos/usage-circle-layout.py index 501f1523..a0fc0d52 100644 --- a/demos/usage-circle-layout.py +++ b/demos/usage-circle-layout.py @@ -1,6 +1,5 @@ """ Original Demo: http://js.cytoscape.org/demos/circle-layout/ -Original Code: https://github.com/cytoscape/cytoscape.js/tree/master/documentation/demos/circle-layout """ import json diff --git a/demos/usage-compound-nodes.py b/demos/usage-compound-nodes.py index 87a8694a..3087a3be 100644 --- a/demos/usage-compound-nodes.py +++ b/demos/usage-compound-nodes.py @@ -1,6 +1,5 @@ """ Original Demo: http://js.cytoscape.org/demos/compound-nodes/ -Original Code: https://github.com/cytoscape/cytoscape.js/tree/master/documentation/demos/compound-nodes Note: The Dash version is also uncentered. Otherwise it works. """ diff --git a/demos/usage-concentric-layout.py b/demos/usage-concentric-layout.py index 82181a4d..f6caca7f 100644 --- a/demos/usage-concentric-layout.py +++ b/demos/usage-concentric-layout.py @@ -1,6 +1,5 @@ """ Original Demo: http://js.cytoscape.org/demos/concentric-layout/ -Original Code: https://github.com/cytoscape/cytoscape.js/blob/master/documentation/demos/concentric-layout/code.js Note: This example is broken because layout takes a function as input, i.e. ``` diff --git a/demos/usage-concentric-social-network.py b/demos/usage-concentric-social-network.py index 7a335f7a..29e721b0 100644 --- a/demos/usage-concentric-social-network.py +++ b/demos/usage-concentric-social-network.py @@ -70,4 +70,4 @@ if __name__ == '__main__': - app.run_server(debug=True) \ No newline at end of file + app.run_server(debug=True) diff --git a/demos/usage-cose-bilkent-layout.py b/demos/usage-cose-bilkent-layout.py index 40934819..44d973ab 100644 --- a/demos/usage-cose-bilkent-layout.py +++ b/demos/usage-cose-bilkent-layout.py @@ -1,6 +1,5 @@ """ Original Demo: http://js.cytoscape.org/demos/cose-bilkent-layout-compound/ -Original Code: https://github.com/cytoscape/cytoscape.js/tree/master/documentation/demos/cose-bilkent-layout-compound Note: This implementation DOES NOT work yet, since cose-bilkent hasn't been implemented yet @@ -16,7 +15,8 @@ server = app.server app.scripts.append_script({ - 'external_url': 'https://cdn.rawgit.com/cytoscape/cytoscape.js-cose-bilkent/d810281d/cytoscape-cose-bilkent.js' + 'external_url': ('https://cdn.rawgit.com/cytoscape/cytoscape.js-cose-bilkent/d810281d/' + 'cytoscape-cose-bilkent.js') }) app.scripts.config.serve_locally = True diff --git a/demos/usage-cose-layout.py b/demos/usage-cose-layout.py index 175afa65..63b0dee5 100644 --- a/demos/usage-cose-layout.py +++ b/demos/usage-cose-layout.py @@ -1,6 +1,5 @@ """ Original Demo: http://js.cytoscape.org/demos/cose-layout/ -Original Code: https://github.com/cytoscape/cytoscape.js/tree/master/documentation/demos/cose-layout Note: This implementation looks different from the original implementation, although the input paramters are exactly the same. diff --git a/demos/usage-edge-types.py b/demos/usage-edge-types.py index 9737c781..a1e12fab 100644 --- a/demos/usage-edge-types.py +++ b/demos/usage-edge-types.py @@ -1,6 +1,5 @@ """ Original Demo: http://js.cytoscape.org/demos/edge-types/ -Original Code: https://github.com/cytoscape/cytoscape.js/tree/master/documentation/demos/edge-types Note: This example is broken because layout takes a function as input: ``` diff --git a/demos/usage-grid-layout.py b/demos/usage-grid-layout.py index 5e37bfcd..44621a1b 100644 --- a/demos/usage-grid-layout.py +++ b/demos/usage-grid-layout.py @@ -1,6 +1,5 @@ """ Original Demo: http://js.cytoscape.org/demos/grid-layout/ -Original Code: https://github.com/cytoscape/cytoscape.js/tree/master/documentation/demos/grid-layout """ import json diff --git a/demos/usage-grid-social-network.py b/demos/usage-grid-social-network.py index 4dc12de5..05341d9c 100644 --- a/demos/usage-grid-social-network.py +++ b/demos/usage-grid-social-network.py @@ -73,4 +73,4 @@ if __name__ == '__main__': - app.run_server(debug=True) \ No newline at end of file + app.run_server(debug=True) diff --git a/demos/usage-initialisation.py b/demos/usage-initialisation.py index f3cec4ed..96310474 100644 --- a/demos/usage-initialisation.py +++ b/demos/usage-initialisation.py @@ -1,6 +1,5 @@ """ Original Demo: http://js.cytoscape.org/demos/initialisation/ -Original Code: https://github.com/cytoscape/cytoscape.js/blob/master/documentation/demos/initialisation/code.js Note: The click-and-drag functionality is broken in this Dash implementation because the example requires a function referring to the "cy" property, i.e. diff --git a/demos/usage-labels.py b/demos/usage-labels.py index 33d7014f..42902aeb 100644 --- a/demos/usage-labels.py +++ b/demos/usage-labels.py @@ -1,6 +1,5 @@ """ Original Demo: http://js.cytoscape.org/demos/labels/ -Original Code: https://github.com/cytoscape/cytoscape.js/tree/master/documentation/demos/labels Note: This example is broken because layout takes a function as input: ``` @@ -51,7 +50,8 @@ {'data': {'id': 'ar-src'}}, {'data': {'id': 'ar-tgt'}}, - {'data': {'source': 'ar-src', 'target': 'ar-tgt', 'label': 'autorotate (move my nodes)'}, 'classes': 'autorotate'}, + {'data': {'source': 'ar-src', 'target': 'ar-tgt', 'label': 'autorotate (move my nodes)'}, + 'classes': 'autorotate'}, {'data': {'label': 'background'}, 'classes': 'background'} ] diff --git a/demos/usage-linkout-example.py b/demos/usage-linkout-example.py index 1f2e0af8..a3cac8d7 100644 --- a/demos/usage-linkout-example.py +++ b/demos/usage-linkout-example.py @@ -1,6 +1,5 @@ """ Original Demo: http://js.cytoscape.org/demos/linkout-example/ -Original Code: https://github.com/cytoscape/cytoscape.js/blob/master/documentation/demos/linkout-example/ Note: Href Links do not work diff --git a/demos/usage-multiple-instances.py b/demos/usage-multiple-instances.py index 7766fa5e..91718ff0 100644 --- a/demos/usage-multiple-instances.py +++ b/demos/usage-multiple-instances.py @@ -1,6 +1,5 @@ """ Original Demo: http://js.cytoscape.org/demos/multiple-instances/ -Original Code: https://github.com/cytoscape/cytoscape.js/blob/master/documentation/demos/multiple-instances """ import dash import dash_html_components as html diff --git a/demos/usage-phylogeny.py b/demos/usage-phylogeny.py index 813364f9..5a2f26c9 100644 --- a/demos/usage-phylogeny.py +++ b/demos/usage-phylogeny.py @@ -1,10 +1,10 @@ +# pylint: disable=W0621 import math import dash -import dash_html_components as html from dash.dependencies import Input, Output - import dash_cytoscape as cyto +import dash_html_components as html try: from Bio import Phylo @@ -16,13 +16,13 @@ def generate_elements(tree, xlen=30, ylen=30, grabbable=False): def get_col_positions(tree, column_width=80): + """Create a mapping of each clade to its column position.""" taxa = tree.get_terminals() # Some constants for the drawing calculations max_label_width = max(len(str(taxon)) for taxon in taxa) drawing_width = column_width - max_label_width - 1 - """Create a mapping of each clade to its column position.""" depths = tree.depths() # If there are no branch lengths, assume unit branch lengths if not max(depths.values()): @@ -164,7 +164,6 @@ def add_to_elements(clade, clade_id): app = dash.Dash(__name__) server = app.server - app.layout = html.Div([ cyto.Cytoscape( id='cytoscape', diff --git a/demos/usage-pie-style.py b/demos/usage-pie-style.py index 4f1aa3f3..356a01fb 100644 --- a/demos/usage-pie-style.py +++ b/demos/usage-pie-style.py @@ -1,6 +1,5 @@ """ Original Demo: http://js.cytoscape.org/demos/pie-style/ -Original Code: https://github.com/cytoscape/cytoscape.js/blob/master/documentation/demos/pie-style """ import dash import dash_html_components as html diff --git a/demos/usage-visual-style.py b/demos/usage-visual-style.py index 389f82c4..846f2db1 100644 --- a/demos/usage-visual-style.py +++ b/demos/usage-visual-style.py @@ -1,6 +1,5 @@ """ Original Demo: http://js.cytoscape.org/demos/visual-style/ -Original Code: https://github.com/cytoscape/cytoscape.js/tree/master/documentation/demos/visual-style """ import dash import dash_html_components as html diff --git a/usage-elements.py b/usage-elements.py index d8134c9e..3df16849 100644 --- a/usage-elements.py +++ b/usage-elements.py @@ -1,9 +1,9 @@ import json import dash +from dash.dependencies import Input, Output, State import dash_core_components as dcc import dash_html_components as html -from dash.dependencies import Input, Output, State import dash_cytoscape as cyto from demos import dash_reusable_components as drc @@ -15,10 +15,10 @@ # ###################### DATA PREPROCESSING ###################### # Load data with open('demos/data/sample_network.txt', 'r') as f: - data = f.read().split('\n') + network_data = f.read().split('\n') # We select the first 750 edges and associated nodes for an easier visualization -edges = data[:750] +edges = network_data[:750] nodes = set() following_node_di = {} # user id -> list of users they are following @@ -263,8 +263,8 @@ def generate_elements(nodeData, elements, expansion_mode): elements.extend(followers_nodes) if followers_edges: - for edge in followers_edges: - edge['classes'] = 'followerEdge' + for follower_edge in followers_edges: + follower_edge['classes'] = 'followerEdge' elements.extend(followers_edges) elif expansion_mode == 'following': @@ -279,8 +279,8 @@ def generate_elements(nodeData, elements, expansion_mode): elements.append(node) if following_edges: - for edge in following_edges: - edge['classes'] = 'followingEdge' + for follower_edge in following_edges: + follower_edge['classes'] = 'followingEdge' elements.extend(following_edges) return elements diff --git a/usage-events.py b/usage-events.py index 0e0af921..94b2089d 100644 --- a/usage-events.py +++ b/usage-events.py @@ -1,9 +1,9 @@ import json import dash +from dash.dependencies import Input, Output import dash_core_components as dcc import dash_html_components as html -from dash.dependencies import Input, Output import dash_cytoscape as cyto diff --git a/usage-stylesheet.py b/usage-stylesheet.py index cd1152d0..8dd292cd 100644 --- a/usage-stylesheet.py +++ b/usage-stylesheet.py @@ -1,9 +1,9 @@ import json import dash +from dash.dependencies import Input, Output import dash_core_components as dcc import dash_html_components as html -from dash.dependencies import Input, Output import dash_cytoscape as cyto from demos import dash_reusable_components as drc @@ -14,17 +14,17 @@ # ###################### DATA PREPROCESSING ###################### # Load data with open('demos/data/sample_network.txt', 'r') as f: - data = f.read().split('\n') + network_data = f.read().split('\n') # We select the first 750 edges and associated nodes for an easier visualization -edges = data[:750] +edges = network_data[:750] nodes = set() cy_edges = [] cy_nodes = [] -for edge in edges: - source, target = edge.split(" ") +for network_edge in edges: + source, target = network_edge.split(" ") if source not in nodes: nodes.add(source) @@ -93,7 +93,7 @@ 'breadthfirst', 'cose' ), - value='random', + value='grid', clearable=False ), From 1f4aee5be0bc0bebb2ca27c68145372edbcd6145 Mon Sep 17 00:00:00 2001 From: Xing Han Lu Date: Mon, 4 Mar 2019 18:06:04 -0500 Subject: [PATCH 02/12] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4ab8bf3c..95395dfc 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ .idea/**/gradle.xml .idea/**/libraries tests/screenshots/*.png +tests/screenshots/**/*.png # misc .DS_Store From 34f94c53ac67f97f52e996dca1d4545be9ebb4c4 Mon Sep 17 00:00:00 2001 From: Xing Han Lu Date: Mon, 4 Mar 2019 18:06:29 -0500 Subject: [PATCH 03/12] Add biopython to requirements.txt --- tests/requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/requirements.txt b/tests/requirements.txt index 7e492fb4..919d94b5 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -13,4 +13,5 @@ selenium flake8 pylint pytest-dash>=1.0.1 -colour==0.1.5 \ No newline at end of file +colour==0.1.5 +biopython \ No newline at end of file From 1dc590a0de5610b6d378ab24c026a78db2a1f3e9 Mon Sep 17 00:00:00 2001 From: Xing Han Lu Date: Mon, 4 Mar 2019 18:08:06 -0500 Subject: [PATCH 04/12] Move Percy initialization and methods to `test_percy_snapshot` --- tests/IntegrationTests.py | 25 ++++--------------- tests/test_percy_snapshot.py | 47 +++++++++++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 23 deletions(-) diff --git a/tests/IntegrationTests.py b/tests/IntegrationTests.py index e3237de8..ea17c277 100644 --- a/tests/IntegrationTests.py +++ b/tests/IntegrationTests.py @@ -3,27 +3,21 @@ import logging import os import multiprocessing -import sys import time import unittest -import percy import threading import platform import flask -import requests from selenium import webdriver from selenium.webdriver.chrome.options import Options class IntegrationTests(unittest.TestCase): - def percy_snapshot(self, name=''): - if os.environ.get('PERCY_ENABLED', False): - snapshot_name = '{} - {}'.format(name, sys.version_info) - - self.percy_runner.snapshot( - name=snapshot_name - ) + """ + Percy is not initialized here since some tests that inherits from + IntegrationTests do not need to run Percy tests + """ @classmethod def setUpClass(cls): @@ -36,19 +30,11 @@ def setUpClass(cls): cls.driver = webdriver.Chrome(options=options) cls.driver.set_window_size(1280, 1000) - if os.environ.get('PERCY_ENABLED', False): - loader = percy.ResourceLoader(webdriver=cls.driver) - percy_config = percy.Config(default_widths=[1280]) - cls.percy_runner = percy.Runner(loader=loader, config=percy_config) - cls.percy_runner.initialize_build() - @classmethod def tearDownClass(cls): super(IntegrationTests, cls).tearDownClass() cls.driver.quit() - if os.environ.get('PERCY_ENABLED', False): - cls.percy_runner.finalize_build() def setUp(self): pass @@ -56,8 +42,7 @@ def setUp(self): def tearDown(self): time.sleep(3) if platform.system() == 'Windows': - requests.get('http://localhost:8050/stop') - sys.exit() + self.driver.get('http://localhost:8050/stop') else: self.server_process.terminate() time.sleep(3) diff --git a/tests/test_percy_snapshot.py b/tests/test_percy_snapshot.py index 72a20268..337c2912 100644 --- a/tests/test_percy_snapshot.py +++ b/tests/test_percy_snapshot.py @@ -1,11 +1,13 @@ import base64 import os +import sys import time from .IntegrationTests import IntegrationTests import dash import dash_html_components as html import dash_core_components as dcc +import percy from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC @@ -21,13 +23,23 @@ class Tests(IntegrationTests): Instead, we use Selenium webdrivers to automatically screenshot each of the apps being tested in test_usage.py, display them in a simple Dash app, and use Percy to take a snapshot for CVI. + + Here, we extend the setUpClass method from IntegrationTests by adding + percy runner initialization. This is because other classes that inherits + from IntegrationTests do not necessarily need to initialize Percy (since + all they do is save snapshots), and doing so causes Percy to render an + empty build that ends up failing. Therefore, we decide to initialize and + finalize the Percy runner in this class rather than inside + IntegrationTests. """ - def test_usage(self): + @staticmethod + def create_app(dir_name): def encode(name): path = os.path.join( os.path.dirname(__file__), 'screenshots', + dir_name, name ) @@ -60,8 +72,37 @@ def display_image(pathname): # pylint: disable=W0612 name = pathname.replace('/', '') return html.Img(id=name, src=encode(name)) - # Start the app - self.startServer(app) + return app + + def percy_snapshot(self, name=''): + if os.environ.get('PERCY_ENABLED', False): + snapshot_name = '{} (Python {}.{}.{})'.format( + name, + sys.version_info.major, + sys.version_info.minor, + sys.version_info.micro, + ) + + self.percy_runner.snapshot( + name=snapshot_name + ) + + @classmethod + def setUpClass(cls): + super(Tests, cls).setUpClass() + + if os.environ.get('PERCY_ENABLED', False): + loader = percy.ResourceLoader(webdriver=cls.driver) + percy_config = percy.Config(default_widths=[1280]) + cls.percy_runner = percy.Runner(loader=loader, config=percy_config) + cls.percy_runner.initialize_build() + + @classmethod + def tearDownClass(cls): + super(Tests, cls).tearDownClass() + + if os.environ.get('PERCY_ENABLED', False): + cls.percy_runner.finalize_build() # Find the names of all the screenshots asset_list = os.listdir(os.path.join( From 586278b0cde9d2380f7eb3187b265e80c79ada67 Mon Sep 17 00:00:00 2001 From: Xing Han Lu Date: Mon, 4 Mar 2019 18:08:25 -0500 Subject: [PATCH 05/12] Create test_callbacks.py --- tests/test_callbacks.py | 332 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 tests/test_callbacks.py diff --git a/tests/test_callbacks.py b/tests/test_callbacks.py new file mode 100644 index 00000000..f1d96cf4 --- /dev/null +++ b/tests/test_callbacks.py @@ -0,0 +1,332 @@ +import os +import importlib + +from .IntegrationTests import IntegrationTests +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.common.keys import Keys + + +class Tests(IntegrationTests): + def test_callbacks(self): + app = importlib.import_module('usage-advanced').app + self.startServer(app) + + WebDriverWait(self.driver, 20).until( + EC.presence_of_element_located((By.ID, "cytoscape")) + ) + + def create_input_and_save(css_selector, + dir_name, + options, + prefix=None, + name_map=None, + save=True): + elem = self.driver.find_element_by_css_selector(css_selector) + + directory_path = os.path.join( + os.path.dirname(__file__), + 'screenshots', + dir_name + ) + + # Create directory if it doesn't already exist + if not os.path.exists(directory_path): + os.makedirs(directory_path) + + if prefix and not name_map: + name_map = { + option: prefix + option + for option in options + } + + elif not name_map: + name_map = {} + + for option in options: + elem.send_keys(Keys.CONTROL + "a") + elem.send_keys(option) + elem.send_keys(Keys.RETURN) + + if save: + # If the name map doesn't contain a custom name for the option, + # we default to the value of the option to name the saved + # screenshot + name = name_map.get(option, option) + name = name.replace('(', '_').replace(')', '').replace(',', '_') + name = name.replace('#', '_hex_').replace(' ', '') + + WebDriverWait(self.driver, 20).until( + EC.presence_of_element_located((By.ID, "cytoscape")) + ) + + path = os.path.join( + os.path.dirname(__file__), + 'screenshots', + dir_name, + name + '.png' + ) + + self.driver.save_screenshot(path) + + def click_button_and_save(name_to_xpaths, dir_name, save=True): + for name, xpath in name_to_xpaths.items(): + button = self.driver.find_element(By.XPATH, xpath) + button.click() + + if save: + WebDriverWait(self.driver, 20).until( + EC.presence_of_element_located((By.ID, "cytoscape")) + ) + + path = os.path.join( + os.path.dirname(__file__), + 'screenshots', + dir_name, + name + '.png' + ) + + self.driver.save_screenshot(path) + + create_input_and_save( + css_selector='input#dropdown-select-element-list', + dir_name='elements', + options=['Basic', 'Compound', 'Gene', 'Wineandcheese'] + ) + + create_input_and_save( + css_selector='input#dropdown-layout', + dir_name='layouts', + options=[ + 'Preset', + 'Grid', + 'Circle', + 'Concentric', + 'Breadthfirst', + 'Cose' + ] + ) + + # Reset the input to what it was at the beginning + create_input_and_save( + css_selector='input#dropdown-select-element-list', + dir_name='elements', + options=['Basic'], + save=False + ) + create_input_and_save( + css_selector='input#dropdown-layout', + dir_name='layouts', + options=['Circle'], + save=False + ) + + # Input Different types of Node Content + create_input_and_save( + css_selector='input#input-node-content', + dir_name='style', + options=[ + 'Hello', + 'data(id)' + ], + name_map={ + 'Hello': 'NodeDisplayContentStatic', + 'data(id)': 'NodeDisplayID' + } + ) + + # Input Different node widths + create_input_and_save( + css_selector='input#input-node-width', + dir_name='style', + options=[ + '30', + '50' + ], + prefix='NodeWidth' + ) + + # Input Different node heights + create_input_and_save( + css_selector='input#input-node-height', + dir_name='style', + options=[ + '40', + '60' + ], + prefix='NodeHeight' + ) + + # Input different node shapes + create_input_and_save( + css_selector='input#dropdown-node-shape', + dir_name='style', + options=[ + 'Triangle', + 'Rectangle', + 'Roundrectangle', + 'Barrel', + 'Diamond', + 'Pentagon', + 'Star', + 'Tag', + 'Vee', + 'Ellipse' + ], + prefix='NodeShape' + ) + + create_input_and_save( + css_selector='input#input-node-color', + dir_name='style', + options=[ + 'pink', + 'sky blue', + 'rgb(186,44,162)', + '#def229' + ], + prefix='NodeColor' + ) + + create_input_and_save( + css_selector='input#input-node-border-width', + dir_name='style', + options=['2'], + save=False + ) + + create_input_and_save( + css_selector='input#input-node-border-color', + dir_name='style', + options=[ + 'pink', + 'sky blue', + 'rgb(186,44,162)', + '#def229' + ], + prefix='BorderColor' + ) + + create_input_and_save( + css_selector='input#input-node-border-width', + dir_name='style', + options=['5', '2'], + prefix='NodeBorderWidth' + ) + + create_input_and_save( + css_selector='input#dropdown-node-border-style', + dir_name='style', + options=[ + 'Dashed', + 'Dotted', + 'Double', + 'Solid', + ], + prefix='NodeBorderStyle' + ) + + create_input_and_save( + css_selector='input#input-node-padding', + dir_name='style', + options=['5px'], + prefix='NodePadding' + ) + + create_input_and_save( + css_selector='input#dropdown-node-padding-relative-to', + dir_name='style', + options=['Width', 'Height', 'Average', 'Min', 'Max'], + prefix='NodePaddingRelativeTo' + ) + + create_input_and_save( + css_selector='input#input-edge-line-width', + dir_name='style', + options=['10', '1', '3'], + prefix='LineWidth' + ) + + create_input_and_save( + css_selector='input#dropdown-edge-curve-style', + dir_name='style', + options=['Haystack', 'Segments', 'Unbundled-bezier', 'Bezier'], + prefix='EdgeCurveStyle' + ) + + create_input_and_save( + css_selector='input#input-edge-line-color', + dir_name='style', + options=[ + 'pink', + 'sky blue', + 'rgb(186,44,162)', + '#def229' + ], + prefix='EdgeColor' + ) + + # Modify Edge Styles + click_button_and_save( + name_to_xpaths={ + 'EdgeStyleSolid': '//*[@id="radio-edge-line-style"]/label[1]', + 'EdgeStyleDotted': '//*[@id="radio-edge-line-style"]/label[2]', + 'EdgeStyleDashed': '//*[@id="radio-edge-line-style"]/label[3]', + }, + dir_name='style' + ) + + # Set "Use Edge Arrow" to "Yes" + click_button_and_save( + name_to_xpaths={ + 'EdgeArrow': '//*[@id="radio-use-edge-arrow"]/label[1]'}, + dir_name='style', + save=False + ) + + create_input_and_save( + css_selector='input#dropdown-source-arrow-shape', + dir_name='style', + options=[ + 'Circle', + 'Vee', + 'Tee', + 'Diamond', + 'Triangle' + ], + prefix='EdgeArrowShape' + ) + + create_input_and_save( + css_selector='input#input-source-arrow-color', + dir_name='style', + options=[ + 'pink', + 'sky blue', + 'rgb(186,44,162)', + '#def229' + ], + prefix='EdgeArrowColor' + ) + + click_button_and_save( + name_to_xpaths={ + 'EdgeArrowFilled': ('//*[@id="radio-source-arrow-fill"]/' + 'label[1]'), + 'EdgeArrowHollow': ('//*[@id="radio-source-arrow-fill"]/' + 'label[2]') + }, + dir_name='style' + ) + + create_input_and_save( + css_selector='input#input-arrow-scale', + dir_name='style', + options=[ + '3', + '2', + '1' + ], + prefix='EdgeArrowScale' + ) From ca54a1e0bffeebe9d783a5154886d9af82b17703 Mon Sep 17 00:00:00 2001 From: Xing Han Lu Date: Mon, 4 Mar 2019 18:08:31 -0500 Subject: [PATCH 06/12] Create test_interactions.py --- tests/test_interactions.py | 153 +++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 tests/test_interactions.py diff --git a/tests/test_interactions.py b/tests/test_interactions.py new file mode 100644 index 00000000..cb5f0aa3 --- /dev/null +++ b/tests/test_interactions.py @@ -0,0 +1,153 @@ +import os +import importlib +import time +import json + +from .IntegrationTests import IntegrationTests +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.common.action_chains import ActionChains + + +class Tests(IntegrationTests): + def test_interactions(self): + app = importlib.import_module('usage-events').app + self.startServer(app) + WebDriverWait(self.driver, 20).until(EC.presence_of_element_located((By.ID, "cytoscape"))) + + actions = ActionChains(self.driver) + + def save_screenshot(dir_name, name): + directory_path = os.path.join( + os.path.dirname(__file__), + 'screenshots', + dir_name + ) + + # Create directory if it doesn't already exist + if not os.path.exists(directory_path): + os.makedirs(directory_path) + + self.driver.save_screenshot(os.path.join( + os.path.dirname(__file__), + 'screenshots', + dir_name, + name + '.png' + )) + + def perform_dragging(x, y, delta_x, delta_y, elem, dir_name='interactions'): + """ + Performs dragging on a node, and return the difference from the start + :param x: initial position of the node at the start of the action chain + :param y: initial position of the node at the start of the action chain + :param delta_x: how much we want to drag the node + :param delta_y: how much we want to drag the node + :param dir_name: The directory in which we store our screenshots + :return: the difference between the position after drag and the starting position + """ + actions.reset_actions() + actions.move_to_element_with_offset( + self.driver.find_element_by_tag_name('body'), x, y + ) + actions.drag_and_drop_by_offset(source=None, xoffset=delta_x, yoffset=delta_y) + actions.click() + actions.perform() + time.sleep(1) + + elem_json = json.loads(elem.text) + new_pos = elem_json.get('renderedPosition') + clicked_label = elem_json.get('data', {}).get('label') + + diff_x = round(new_pos['x'] - x) + diff_y = round(new_pos['y'] - y) + + save_screenshot( + dir_name, + 'Dragged{}By{}x{}y'.format(clicked_label.replace(' ', ''), diff_x, diff_y) + ) + + return diff_x, diff_y + + def perform_clicking(x, y, elem, dir_name='interactions'): + """ + :param x: The position on the screen where we want to click + :param y: The position on the screen where we want to click + :param elem: The element object from where we retrieve the JSON + :param dir_name: The directory in which we store our screenshots + :return: The label of element most recently clicked, if any + """ + actions.reset_actions() + actions.move_to_element_with_offset( + self.driver.find_element_by_tag_name('body'), x, y + ) + actions.click() + actions.perform() + + time.sleep(1) + clicked_label = json.loads(elem.text).get('data', {}).get('label') + + save_screenshot(dir_name, 'Clicked' + clicked_label.replace(' ', '')) + + return clicked_label + + def perform_mouseover(x, y, elem, dir_name='interactions'): + actions.reset_actions() + actions.move_to_element_with_offset( + self.driver.find_element_by_tag_name('body'), x - 100, y + ) + actions.move_by_offset(100, 0) + actions.perform() + time.sleep(1) + + mouseover_label = json.loads(elem.text).get('label') + + save_screenshot(dir_name, 'Mouseover' + mouseover_label.replace(' ', '')) + + return mouseover_label + + drag_error = "Unable to drag Cytoscape nodes properly" + click_error = "Unable to click Cytoscape nodes properly" + mouseover_error = "Unable to mouseover Cytoscape nodes properly" + + init_pos = { + 'Node 1': (80.94611044209678, 333.54879281525285), + 'Node 2': (375.64032747402433, 628.2430098471805), + 'Node 3': (277.40892179671516, 514.2945792615018 - 20), + 'Node 4': (768.5659501832611, 333.54879281525285), + 'Node 5': (473.8717331513335, 431.780198492562), + 'Node 6': (277.40892179671516, 530.0116041698712) + } + init_x, init_y = init_pos['Node 1'] + # Select the JSON output element + elem_tap = self.driver.find_element_by_css_selector('pre#tap-node-json-output') + + # # Test dragging the nodes around + offset_x, offset_y = perform_dragging(init_x, init_y, 0, 0, elem_tap) + init_x += offset_x + init_y += offset_y + + assert perform_dragging(init_x, init_y, 150, 0, elem_tap) == (150, 0), drag_error + assert perform_dragging(init_x+150, init_y, 0, 150, elem_tap) == (0, 150), drag_error + assert perform_dragging(init_x+150, init_y+150, -150, -150, elem_tap) == (-150, -150), \ + drag_error + + # Test clicking the nodes + for i in range(1, 7): + label = 'Node {}'.format(i) + assert perform_clicking(*init_pos[label], elem_tap) == label, click_error + + # Open the Mouseover JSON tab + actions.move_to_element( + self.driver.find_element_by_css_selector('#tabs > div:nth-child(3)')) + actions.click().perform() + time.sleep(1) + + # Select the JSON output element + elem_mouseover = self.driver.find_element_by_css_selector( + 'pre#mouseover-node-data-json-output') + + # Test hovering the nodes + for i in range(1, 7): + label = 'Node {}'.format(i) + assert perform_mouseover(*init_pos[label], elem_mouseover) == label, mouseover_error From 48110f9273d3ddc70e36c0b574c57aa9e41c0e1d Mon Sep 17 00:00:00 2001 From: Xing Han Lu Date: Mon, 4 Mar 2019 18:09:41 -0500 Subject: [PATCH 07/12] Update saving directory for test_usage tests/screenshots -> tests/screenshots/usage --- tests/test_usage.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/test_usage.py b/tests/test_usage.py index 4fa3ab1b..0566a360 100644 --- a/tests/test_usage.py +++ b/tests/test_usage.py @@ -7,7 +7,7 @@ class Tests(IntegrationTests): - def create_usage_test(self, filename): + def create_usage_test(self, filename, dir_name='usage'): app = importlib.import_module(filename).app self.startServer(app) @@ -16,9 +16,20 @@ def create_usage_test(self, filename): EC.presence_of_element_located((By.ID, "cytoscape")) ) + directory_path = os.path.join( + os.path.dirname(__file__), + 'screenshots', + dir_name + ) + + # Create directory if it doesn't already exist + if not os.path.exists(directory_path): + os.makedirs(directory_path) + self.driver.save_screenshot(os.path.join( os.path.dirname(__file__), 'screenshots', + dir_name, filename + '.png' )) From 4f93f72431dc496233148543c6bd2538e89ade91 Mon Sep 17 00:00:00 2001 From: Xing Han Lu Date: Mon, 4 Mar 2019 18:10:23 -0500 Subject: [PATCH 08/12] Add a snapshot build for all tests --- tests/test_percy_snapshot.py | 41 ++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/tests/test_percy_snapshot.py b/tests/test_percy_snapshot.py index 337c2912..ddfa8cff 100644 --- a/tests/test_percy_snapshot.py +++ b/tests/test_percy_snapshot.py @@ -104,20 +104,57 @@ def tearDownClass(cls): if os.environ.get('PERCY_ENABLED', False): cls.percy_runner.finalize_build() + def run_percy_on(self, dir_name): # Find the names of all the screenshots asset_list = os.listdir(os.path.join( os.path.dirname(__file__), - 'screenshots' + 'screenshots', + dir_name )) # Run Percy for image in asset_list: if image.endswith('png'): + output_name = image.replace('.png', '') + self.driver.get('http://localhost:8050/{}'.format(image)) WebDriverWait(self.driver, 20).until( EC.presence_of_element_located((By.ID, image)) ) - self.percy_snapshot(name=image) + self.percy_snapshot( + name='{}: {}'.format(dir_name.upper(), output_name) + ) time.sleep(2) + + def test_usage(self): + # Create and start the app + app = self.create_app(dir_name='usage') + self.startServer(app) + + self.run_percy_on('usage') + + def test_elements(self): + app = self.create_app(dir_name='elements') + self.startServer(app) + + self.run_percy_on('elements') + + def test_layouts(self): + app = self.create_app(dir_name='layouts') + self.startServer(app) + + self.run_percy_on('layouts') + + def test_style(self): + app = self.create_app(dir_name='style') + self.startServer(app) + + self.run_percy_on('style') + + def test_interactions(self): + app = self.create_app(dir_name='interactions') + self.startServer(app) + + self.run_percy_on('interactions') From 71405b51361829d11175c43295da260466070ab9 Mon Sep 17 00:00:00 2001 From: Xing Han Lu Date: Mon, 4 Mar 2019 18:10:51 -0500 Subject: [PATCH 09/12] Add new test runs to config, update flake8/pylint --- .circleci/config.yml | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 72ef31f7..abd233ce 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -59,23 +59,39 @@ jobs: name: Run pylint command: | . venv/bin/activate - pylint usage.py tests + pylint usage.py usage-advanced.py usage-elements.py usage-events.py usage-stylesheet.py + pylint demos tests when: always - run: name: Run flake8 command: | . venv/bin/activate - flake8 usage.py tests + flake8 --max-line-length=100 usage.py usage-advanced.py usage-elements.py usage-events.py usage-stylesheet.py + flake8 --max-line-length=100 demos tests when: always - run: - name: Integration Tests - Usage + name: Integration Tests - Usage Apps Rendering command: | . venv/bin/activate python -m unittest tests.test_usage when: always + - run: + name: Integration Tests - Interactions + command: | + . venv/bin/activate + python -m unittest tests.test_interactions + when: always + + - run: + name: Integration Tests - Callbacks + command: | + . venv/bin/activate + python -m unittest tests.test_callbacks + when: always + - run: name: Capture Percy Snapshots command: | From 473ac4f6044a5361ad473eb81a48fa036137fe79 Mon Sep 17 00:00:00 2001 From: Xing Han Lu Date: Mon, 4 Mar 2019 18:11:00 -0500 Subject: [PATCH 10/12] Update CHANGELOG.md --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad222b79..fe2545db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## Added +* `tests.test_usage`: Tests for rendering usage files. +* `tests.test_callbacks`: Tests for updating `Cytoscape` with callbacks. +* `tests.test_interactions`: Tests for interacting with `Cytoscape`, and evaluating its event callbacks. +* `tests.test_percy_snapshot`: Creates a Percy build using screenshots from other tests. + +## Changed +* `config.yml`: Added steps to run the new tests. Added coverage for Python 3.7. Included `demos` and all usage examples in `pylint` and `flake8`. Increased line limit to 100. +* `demos/usage-*`: Formatted all demo apps in order to respect pylint and flake8. +* `usage-*`: Formatted all demo apps in order to respect pylint and flake8. +* `package.json`: Removed `"prepublish": "npm run validate-init"` due to conflict with CircleCI build. This script will be deprecated in favor of the upcoming Dash Component CLI. +* `tests/IntegrationTests.py`: Moved the `percy_snapshot` method to `test_percy_snapshot` in order to avoid duplicate (failing) builds on Percy. Decrease the number of processes to 1. + +## Removed +* `tests.test_render`: Removed unused test + ## Added * Two new demos: `usage-grid-social-network.py` and `usage-concentric-social-network.py` * Add Issue and PR templates for Github (located in `.github`) From 6a9279b8fe4fe0b3e20719d4ec63ca9912d88555 Mon Sep 17 00:00:00 2001 From: Xing Han Lu Date: Fri, 8 Mar 2019 13:07:44 -0500 Subject: [PATCH 11/12] Updates based on PR comments Additionally, changes to the Python versioning avoids version errors with CircleCI, as discussed here: https://github.com/plotly/dash/pull/636 --- demos/editor/callbacks.py | 2 +- tests/test_interactions.py | 54 ++++++++++++++++++++++++++---------- tests/test_percy_snapshot.py | 5 ++-- 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/demos/editor/callbacks.py b/demos/editor/callbacks.py index 17007b16..067f4144 100644 --- a/demos/editor/callbacks.py +++ b/demos/editor/callbacks.py @@ -29,7 +29,7 @@ def validate_positive(value): return min(0, value) -def validate_color(color: str, default='#999999'): +def validate_color(color, default='#999999'): ''' Check if a color is valid, if so returns the color, else return a default color :param color: The color to validate diff --git a/tests/test_interactions.py b/tests/test_interactions.py index cb5f0aa3..53e34a14 100644 --- a/tests/test_interactions.py +++ b/tests/test_interactions.py @@ -1,3 +1,24 @@ +"""Notes for the hardcoded values in the `init_pos` dictionary: + +It's impossible to programatically get the initial rendered positions of the nodes, since we would +need to obtain the node object (in dictionary format), which can be done either by (a) using the +tapNode callback, which paradoxally requires you to click the node, or (b) by making a complex +calculation relative to the size of the screen w.r.t largest coordinate in the list of elements. +But (b) is unreliable, since we do not know how much padding around the graph is required, so it +will likely be off. + +If there is a need to modify the values in `init_pos`, e.g. if the size of the webdriver screen +is changed, you can do the following: + - Run usage-events.py + - Resize window size to 1280x1000, or preferred size (can be manually done or with selenium) + - Tap on a node + - Inside the "Node Object JSON" section, find "renderedPosition" and use the values there + - Repeat this for all the nodes + +Notice also that there's an offset to Node 3's position. This is because it overlaps with +Node 6, so clicking on Node 3 will erroneously show that you clicked Node 6. Therefore, adding an +offset to the y-axis will ensure that the correct node is clicked. +""" import os import importlib import time @@ -12,12 +33,30 @@ class Tests(IntegrationTests): def test_interactions(self): + # VARIABLES + drag_error = "Unable to drag Cytoscape nodes properly" + click_error = "Unable to click Cytoscape nodes properly" + mouseover_error = "Unable to mouseover Cytoscape nodes properly" + + # View module docstring for more information about initial positions + init_pos = { + 'Node 1': (80.94611044209678, 333.54879281525285), + 'Node 2': (375.64032747402433, 628.2430098471805), + 'Node 3': (277.40892179671516, 514.2945792615018 - 20), + 'Node 4': (768.5659501832611, 333.54879281525285), + 'Node 5': (473.8717331513335, 431.780198492562), + 'Node 6': (277.40892179671516, 530.0116041698712) + } + init_x, init_y = init_pos['Node 1'] + + # Initialize the apps app = importlib.import_module('usage-events').app self.startServer(app) WebDriverWait(self.driver, 20).until(EC.presence_of_element_located((By.ID, "cytoscape"))) actions = ActionChains(self.driver) + # FUNCTIONS def save_screenshot(dir_name, name): directory_path = os.path.join( os.path.dirname(__file__), @@ -106,23 +145,10 @@ def perform_mouseover(x, y, elem, dir_name='interactions'): return mouseover_label - drag_error = "Unable to drag Cytoscape nodes properly" - click_error = "Unable to click Cytoscape nodes properly" - mouseover_error = "Unable to mouseover Cytoscape nodes properly" - - init_pos = { - 'Node 1': (80.94611044209678, 333.54879281525285), - 'Node 2': (375.64032747402433, 628.2430098471805), - 'Node 3': (277.40892179671516, 514.2945792615018 - 20), - 'Node 4': (768.5659501832611, 333.54879281525285), - 'Node 5': (473.8717331513335, 431.780198492562), - 'Node 6': (277.40892179671516, 530.0116041698712) - } - init_x, init_y = init_pos['Node 1'] # Select the JSON output element elem_tap = self.driver.find_element_by_css_selector('pre#tap-node-json-output') - # # Test dragging the nodes around + # Test dragging the nodes around offset_x, offset_y = perform_dragging(init_x, init_y, 0, 0, elem_tap) init_x += offset_x init_y += offset_y diff --git a/tests/test_percy_snapshot.py b/tests/test_percy_snapshot.py index ddfa8cff..7b7113d3 100644 --- a/tests/test_percy_snapshot.py +++ b/tests/test_percy_snapshot.py @@ -76,11 +76,10 @@ def display_image(pathname): # pylint: disable=W0612 def percy_snapshot(self, name=''): if os.environ.get('PERCY_ENABLED', False): - snapshot_name = '{} (Python {}.{}.{})'.format( + snapshot_name = '{} (Python {}.{})'.format( name, sys.version_info.major, - sys.version_info.minor, - sys.version_info.micro, + sys.version_info.minor ) self.percy_runner.snapshot( From 13850d20d7797b7cdf596ea0795958c0923d32bb Mon Sep 17 00:00:00 2001 From: Xing Han Lu Date: Fri, 8 Mar 2019 14:55:43 -0500 Subject: [PATCH 12/12] Update test requirements to the latest --- tests/requirements.txt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/requirements.txt b/tests/requirements.txt index 919d94b5..12d169ab 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -3,15 +3,12 @@ # pip install -r requirements.txt chromedriver-binary -dash>=0.31.0 -dash-core-components -dash-html-components -dash-renderer +dash>=0.38.0 ipdb percy selenium flake8 pylint -pytest-dash>=1.0.1 +pytest-dash>=2.1.1 colour==0.1.5 biopython \ No newline at end of file