Skip to content
Permalink
Browse files

Merge pull request #384 from plotly/fix-tests

Add more Input tests, remove duplicate value callbacks.
  • Loading branch information...
T4rk1n committed Dec 4, 2018
2 parents 5b09a8b + 77d5f5b commit 19ac349065f315d94f1e3aee348cad74c8013328
@@ -2,9 +2,14 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [0.40.2] - 2018-12-04
### Fixed
- Put Input value set in onBlur/onSubmit under a debounce check [#384](https://github.com/plotly/dash-core-components/pull/384)

## [0.40.1] - 2018-12-04
### Fixed
- Fixed issue [#390](https://github.com/plotly/dash-core-components/issues/390) by providing better styles for vertical Tabs.

## [0.40.0] - 2018-11-28
### Added
- Add Logout button (dash-deployment-server authentication integration) [#388](https://github.com/plotly/dash-core-components/pull/388)

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -1,6 +1,6 @@
{
"name": "dash-core-components",
"version": "0.40.1",
"version": "0.40.2",
"description": "Core component suite for Dash",
"repository": {
"type": "git",
@@ -1 +1 @@
__version__ = '0.40.1'
__version__ = '0.40.2'
@@ -1,6 +1,6 @@
{
"name": "dash-core-components",
"version": "0.40.1",
"version": "0.40.2",
"description": "Core component suite for Dash",
"repository": {
"type": "git",
@@ -63,24 +63,28 @@ export default class Input extends Component {
fireEvent({event: 'blur'});
}
if (setProps) {
const castValue =
type === 'number' ? Number(value) : value;
setProps({
const payload = {
n_blur: this.props.n_blur + 1,
n_blur_timestamp: new Date(),
value: castValue,
});
};
if (debounce) {
payload.value =
type === 'number' ? Number(value) : value;
}
setProps(payload);
}
}}
onKeyPress={e => {
if (setProps && e.key === 'Enter') {
const castValue =
type === 'number' ? Number(value) : value;
setProps({
const payload = {
n_submit: this.props.n_submit + 1,
n_submit_timestamp: new Date(),
value: castValue,
});
};
if (debounce) {
payload.value =
type === 'number' ? Number(value) : value;
}
setProps(payload);
}
}}
value={value}
@@ -22,6 +22,7 @@
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

from textwrap import dedent
try:
@@ -30,6 +31,7 @@
from urllib.parse import urlparse

from .IntegrationTests import IntegrationTests
from .utils import wait_for

from multiprocessing import Value

@@ -54,10 +56,14 @@ def wait_for_element_by_css_selector(self, selector):
)

def wait_for_text_to_equal(self, selector, assertion_text):
return WebDriverWait(self.driver, TIMEOUT).until(
WebDriverWait(self.driver, TIMEOUT).until(
EC.text_to_be_present_in_element((By.CSS_SELECTOR, selector),
assertion_text)
)
self.assertEqual(
assertion_text,
self.driver.find_element_by_css_selector(selector).text
)

def snapshot(self, name):
if 'PERCY_PROJECT' in os.environ and 'PERCY_TOKEN' in os.environ:
@@ -1468,3 +1474,91 @@ def _insert_cookie(rep):
self.wait_for_text_to_equal('#content', 'Logged out')

self.assertFalse(self.driver.get_cookie('logout-cookie'))

def test_state_and_inputs(self):
app = dash.Dash(__name__)
app.layout = html.Div([
dcc.Input(value='Initial Input', id='input'),
dcc.Input(value='Initial State', id='state'),
html.Div(id='output')
])

call_count = Value('i', 0)

@app.callback(Output('output', 'children'),
inputs=[Input('input', 'value')],
state=[State('state', 'value')])
def update_output(input, state):
call_count.value += 1
return 'input="{}", state="{}"'.format(input, state)

self.startServer(app)
output = lambda: self.driver.find_element_by_id('output')
input = lambda: self.driver.find_element_by_id('input')
state = lambda: self.driver.find_element_by_id('state')

# callback gets called with initial input
wait_for(lambda: call_count.value == 1)
self.assertEqual(
output().text,
'input="Initial Input", state="Initial State"'
)

input().send_keys('x')
wait_for(lambda: call_count.value == 2)
self.assertEqual(
output().text,
'input="Initial Inputx", state="Initial State"')

state().send_keys('x')
time.sleep(0.75)
self.assertEqual(call_count.value, 2)
self.assertEqual(
output().text,
'input="Initial Inputx", state="Initial State"')

input().send_keys('y')
wait_for(lambda: call_count.value == 3)
self.assertEqual(
output().text,
'input="Initial Inputxy", state="Initial Statex"')

def test_simple_callback(self):
app = dash.Dash(__name__)
app.layout = html.Div([
dcc.Input(
id='input',
),
html.Div(
html.Div([
1.5,
None,
'string',
html.Div(id='output-1')
])
)
])

call_count = Value('i', 0)

@app.callback(Output('output-1', 'children'), [Input('input', 'value')])
def update_output(value):
call_count.value = call_count.value + 1
return value

self.startServer(app)

input1 = self.wait_for_element_by_css_selector('#input')
input1.send_keys('hello world')
output1 = self.wait_for_element_by_css_selector('#output-1')
self.wait_for_text_to_equal('#output-1', 'hello world')
output1.click() # Lose focus, no callback sent for value.

self.assertEqual(
call_count.value,
# an initial call to retrieve the first value
1 +
# one for each hello world character
len('hello world')
)

@@ -1,5 +1,12 @@
import time

TIMEOUT = 20


class WaitForTimeout(Exception):
"""This should only be raised inside the `wait_for` function."""
pass


def assert_clean_console(TestClass):
def assert_no_console_errors(TestClass):
@@ -20,3 +27,46 @@ def assert_no_console_warnings(TestClass):

assert_no_console_warnings(TestClass)
assert_no_console_errors(TestClass)


def wait_for(condition_function, get_message=lambda: '', *args, **kwargs):
"""
Waits for condition_function to return True or raises WaitForTimeout.
:param (function) condition_function: Should return True on success.
:param args: Optional args to pass to condition_function.
:param kwargs: Optional kwargs to pass to condition_function.
if `timeout` is in kwargs, it will be used to override TIMEOUT
:raises: WaitForTimeout If condition_function doesn't return True in time.
Usage:
def get_element(selector):
# some code to get some element or return a `False`-y value.
selector = '.js-plotly-plot'
try:
wait_for(get_element, selector)
except WaitForTimeout:
self.fail('element never appeared...')
plot = get_element(selector) # we know it exists.
"""
def wrapped_condition_function():
"""We wrap this to alter the call base on the closure."""
if args and kwargs:
return condition_function(*args, **kwargs)
if args:
return condition_function(*args)
if kwargs:
return condition_function(**kwargs)
return condition_function()

if 'timeout' in kwargs:
timeout = kwargs['timeout']
del kwargs['timeout']
else:
timeout = TIMEOUT

start_time = time.time()
while time.time() < start_time + timeout:
if wrapped_condition_function():
return True
time.sleep(0.5)

raise WaitForTimeout(get_message())

0 comments on commit 19ac349

Please sign in to comment.
You can’t perform that action at this time.