-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from StratoDem/1-initial
1 initial
- Loading branch information
Showing
13 changed files
with
331 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
root = true | ||
|
||
[*] | ||
indent_style = space | ||
indent_size = 4 | ||
insert_final_newline = true | ||
trim_trailing_whitespace = true | ||
end_of_line = lf | ||
charset = utf-8 | ||
|
||
[*.py] | ||
max_line_length = 100 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Created by .ignore support plugin (hsz.mobi) | ||
### VirtualEnv template | ||
# Virtualenv | ||
# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ | ||
.Python | ||
[Bb]in | ||
[Ii]nclude | ||
[Ll]ib | ||
[Ll]ib64 | ||
[Ll]ocal | ||
[Ss]cripts | ||
pyvenv.cfg | ||
.venv | ||
pip-selfcheck.json | ||
|
||
.idea/ | ||
*.egg-info/ | ||
*.whl |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# CHANGELOG | ||
|
||
## 1.0.0 - 2018-03-27 | ||
### Added | ||
- Initial publication |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,88 @@ | ||
# dash-snapshot-testing | ||
Use snapshot testing to test Dash components | ||
Use snapshot testing, inspired by Jest snapshot testing, to test [Dash][] components. | ||
|
||
## Inspiration | ||
Testing a long HTML component output for a Dash application is difficult. | ||
It typically requires hardcoding data or setting up a dummy database. | ||
Using snapshot tests that JSON serialize the Dash component output provide another | ||
easy testing layer to ensure that code refactors/changes do not change the | ||
output unexpectedly. | ||
|
||
To learn more about snapshot testing in general, see a much more elaborate explanation from the [Facebook Jest site](https://facebook.github.io/jest/docs/en/snapshot-testing.html) | ||
|
||
## Usage | ||
```python | ||
import dash_html_components as html | ||
|
||
from snapshot_test import DashSnapshotTestCase | ||
|
||
|
||
class MyUnitTestCase(DashSnapshotTestCase): | ||
def test_component(self): | ||
my_component = html.Div([html.P('wow'), html.Span('this works')], id='test-id') | ||
|
||
self.assertSnapshotEqual(my_component, 'my-test-unique-id') | ||
``` | ||
|
||
This outputs/checks this JSON at `__snapshots__/MyUnitTestCase-my-test-unique-id.json`: | ||
```json | ||
{ | ||
"type": "Div", | ||
"props": { | ||
"id": "test-id", | ||
"children": [ | ||
{ | ||
"type": "P", | ||
"props": {"children": "wow"}, | ||
"namespace": "dash_html_components" | ||
}, | ||
{ | ||
"type": "Span", | ||
"props": {"children": "this works"}, | ||
"namespace": "dash_html_components" | ||
} | ||
] | ||
}, | ||
"namespace": "dash_html_components" | ||
} | ||
``` | ||
|
||
### Setting a custom `snapshots_dir` for the class | ||
```python | ||
class MyOtherUnitTestCase(DashSnapshotTestCase): | ||
snapshots_dir = '__snapshots_2__' | ||
|
||
def test_component(self): | ||
my_component = html.Div([html.P('wow'), html.Span('another one')], id='test-id') | ||
|
||
self.assertSnapshotEqual(my_component, 'my-test-unique-id') | ||
``` | ||
|
||
This outputs/checks this JSON at `__snapshots_2__/MyOtherUnitTestCase-my-test-unique-id.json`: | ||
```json | ||
{ | ||
"type": "Div", | ||
"props": { | ||
"id": "test-id", | ||
"children": [ | ||
{ | ||
"type": "P", | ||
"props": {"children": "wow"}, | ||
"namespace": "dash_html_components" | ||
}, | ||
{ | ||
"type": "Span", | ||
"props": {"children": "another one"}, | ||
"namespace": "dash_html_components" | ||
} | ||
] | ||
}, | ||
"namespace": "dash_html_components" | ||
} | ||
``` | ||
|
||
At its core, this `unittest.TestCase` compares a JSON-serialized Dash component | ||
against a previously stored JSON-serialized Dash component, and checks if the `dict` | ||
objects from `json.loads` are equivalent using `assertEqual`. | ||
|
||
[Dash]: https://github.com/plotly/dash |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{"type": "Div", "props": {"id": "test-id", "children": [{"type": "P", "props": {"children": "wow"}, "namespace": "dash_html_components"}, {"type": "Span", "props": {"children": "another one"}, "namespace": "dash_html_components"}]}, "namespace": "dash_html_components"} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{"type": "Div", "props": {"id": "test-id", "children": [{"type": "P", "props": {"children": "wow"}, "namespace": "dash_html_components"}, {"type": "Span", "props": {"children": "this works"}, "namespace": "dash_html_components"}]}, "namespace": "dash_html_components"} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
-r ./requirements.txt | ||
|
||
dash-html-components | ||
dash-renderer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
dash>=0.19 | ||
plotly>=2.2.3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
""" | ||
StratoDem Analytics : setup | ||
Principal Author(s) : Eric Linden | ||
Secondary Author(s) : | ||
Description : | ||
Notes : | ||
March 27, 2018 | ||
""" | ||
|
||
from setuptools import setup | ||
|
||
setup( | ||
name='dash-snapshot-testing', | ||
version='1.0.0', | ||
author='Michael Clawar, Eric Linden', | ||
author_email='tech@stratodem.com', | ||
packages=['snapshot_test'], | ||
license='(c) 2018 StratoDem Analytics. All rights reserved.', | ||
description='Dash snapshot testing package', | ||
url='https://github.com/StratoDem/dash-snapshot-testing', | ||
install_requires=[ | ||
'dash>=0.19.0', | ||
'plotly>=2.2.3', | ||
]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
""" | ||
StratoDem Analytics : __init__.py | ||
Principal Author(s) : Michael Clawar | ||
Secondary Author(s) : | ||
Description : | ||
Notes : | ||
March 27, 2018 | ||
""" | ||
|
||
from .snapshot_test_case import * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
""" | ||
StratoDem Analytics : __init__.py | ||
Principal Author(s) : Michael Clawar | ||
Secondary Author(s) : | ||
Description : | ||
Notes : | ||
March 27, 2018 | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
""" | ||
StratoDem Analytics : __test_snapshot_test_case | ||
Principal Author(s) : Michael Clawar | ||
Secondary Author(s) : | ||
Description : | ||
Notes : | ||
March 27, 2018 | ||
""" | ||
|
||
|
||
import dash_html_components as html | ||
|
||
from snapshot_test import DashSnapshotTestCase | ||
|
||
|
||
class MyUnitTestCase(DashSnapshotTestCase): | ||
def test_component(self): | ||
my_component = html.Div([html.P('wow'), html.Span('this works')], id='test-id') | ||
|
||
self.assertSnapshotEqual(my_component, 'my-test-unique-id') | ||
|
||
|
||
class MyOtherUnitTestCase(DashSnapshotTestCase): | ||
snapshots_dir = '__snapshots_2__' | ||
|
||
def test_component(self): | ||
my_component = html.Div([html.P('wow'), html.Span('another one')], id='test-id') | ||
|
||
self.assertSnapshotEqual(my_component, 'my-test-unique-id') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
""" | ||
StratoDem Analytics : SnapshotTest | ||
Principal Author(s) : Eric Linden | ||
Secondary Author(s) : | ||
Description : | ||
Notes : | ||
March 26, 2018 | ||
""" | ||
|
||
import json | ||
import os | ||
import plotly.utils | ||
import unittest | ||
|
||
from dash.development.base_component import Component | ||
|
||
|
||
__all__ = ['DashSnapshotTestCase'] | ||
|
||
|
||
class DashSnapshotTestCase(unittest.TestCase): | ||
snapshots_dir = None | ||
|
||
def assertSnapshotEqual(self, component: Component, file_id: str) -> None: | ||
""" | ||
Tests the supplied component against the specified JSON file snapshot, if it exists. | ||
If the component and the snapshot match, the test passes. If the specified file is not | ||
found, it is created and the test passes. This test will only fail if the file already | ||
exists, and the component-as-JSON does not match the contents of the file. | ||
Parameters | ||
---------- | ||
component: Component | ||
The output of a Dash component that will be rendered to the page | ||
file_id: str | ||
A string ID used to distinguish the multiple JSON files that may be used as | ||
part of a single component's test cases | ||
Returns | ||
------- | ||
None | ||
""" | ||
assert isinstance(component, Component), 'Component passed in must be Dash Component' | ||
assert isinstance(file_id, str), 'must pass in a file id to use as unique file ID' | ||
|
||
filename = self.__get_filename(file_id=file_id) | ||
|
||
component_json = component.to_plotly_json() | ||
|
||
if os.path.exists(filename): | ||
# Load a dumped JSON for the passed-in component, to ensure matches standard format | ||
expected_dict = json.loads( | ||
json.dumps(component_json, cls=plotly.utils.PlotlyJSONEncoder)) | ||
self.assertEqual(self.__load_snapshot(filename=filename), expected_dict) | ||
else: | ||
# Component did not already exist, so we'll write to the file | ||
with open(filename, 'w') as file: | ||
json.dump(component_json, file, cls=plotly.utils.PlotlyJSONEncoder) | ||
|
||
def __get_filename(self, file_id: str) -> str: | ||
""" | ||
Builds and returns the path for the specific JSON file used in this test. | ||
Parameters | ||
---------- | ||
file_id: str | ||
A string ID used to distinguish the multiple JSON files that may be used as | ||
part of a single component's test cases | ||
Returns | ||
------- | ||
A string containing the path to the file. | ||
""" | ||
assert isinstance(file_id, str) | ||
|
||
return os.path.join( | ||
self.__get_snapshots_dir(), | ||
'{}-{}.json'.format(self.__class__.__name__, file_id)) | ||
|
||
@staticmethod | ||
def __load_snapshot(filename: str) -> dict: | ||
""" | ||
Opens the JSON file at the specified location and returns its contents in dict form. | ||
Parameters | ||
---------- | ||
filename: str | ||
The path to the JSON file | ||
Returns | ||
------- | ||
A dict of the JSON file contents. | ||
""" | ||
assert isinstance(filename, str) | ||
|
||
with open(filename, 'r') as f: | ||
return json.load(f) | ||
|
||
@classmethod | ||
def __get_snapshots_dir(cls) -> str: | ||
""" | ||
Checks for the existence of the snapshots directory, and creates it if it is not found. | ||
It then returns the directory path. | ||
Returns | ||
------- | ||
A string containing the path of the snapshots directory. | ||
""" | ||
if cls.snapshots_dir is None: | ||
directory = os.path.join(os.curdir, '__snapshots__') | ||
|
||
if not os.path.exists(directory): | ||
os.mkdir(directory) | ||
|
||
return directory | ||
else: | ||
if not os.path.exists(cls.snapshots_dir): | ||
os.mkdir(cls.snapshots_dir) | ||
|
||
return cls.snapshots_dir |