Open mapix opened 9 months ago
@fohrloop The reason I didn't add more tests here is because the current tests are a bit difficult to run on my local machine. If feasible, I will concentrate on adding more tests after more issues are resolved.
The current modification has been running well in my scene for quite a long time, for example:
@du.callback(
output=[
PREVIEW_IMAGE_ID.get_output("children"),
UPLOAD_IMAGE_LOADING_ID.get_output("children"),
LOGIN_MODAL_ID.get_output("is_open"),
],
state=[
SESSION_STORE_ID.get_state("data"),
TOKEN_STORE_ID.get_state("data"),
],
id=UPLOAD_IMAGE_ID.get_identifier(),
)
def callback_on_completion(status: du.UploadStatus, session_id, token):
if session_id != status.upload_id:
return no_update, status_alert("Session mismatch.", color="danger"), no_update
if (not token) or (not is_valid_token(token)):
return no_update, status_alert("Login required.", color="danger"), True
session = Session.load(session_id)
if not session:
return no_update, status_alert("Session not found.", color="danger"), no_update
if status.is_completed:
return (
render_images(session),
status_alert(
f"Successfully uploaded {status.n_uploaded}/{status.n_total}.",
color="green",
),
no_update,
)
return (
render_images(session),
status_alert(f"Uploading {status.n_uploaded}...", color="green"),
no_update,
)
Hi @mapix ,
Glad to see this one getting a fix. Tests would be nice though; what could be done to make testing easier on your local machine?
I don't think I have too much time to contribute on this project, but could we get some user to comment this change? Or someone from your team?
@salvocamiolo / @salvo-camiolo ?
Niko
@fohrloop
The reason for the difficulty with tests is that I always have a critical Case failure on my Mac, but I cannot determine for a while whether it is due to differences in the logic of handling unlink files between Windows and Mac. This error is not related to the current changes. I wonder if other contributors have encountered this issue as well.
Regarding more contributors, @Sisyphus235 from my team will collaborate more on this project.
================================================================================================== test session starts ==================================================================================================
platform darwin -- Python 3.10.12, pytest-7.4.3, pluggy-1.3.0 -- /Users/wfluo/miniconda3/envs/dash/bin/python
cachedir: .pytest_cache
rootdir: /Users/wfluo/workspaces/dash-uploader
configfile: pytest.ini
testpaths: tests/
plugins: anyio-3.7.1, dash-2.13.0
collected 8 items
tests/test_chromedriver.py::test_chromedriver_version_okay PASSED [ 12%]
tests/test_usage.py::test_render01_render_component PASSED [ 25%]
tests/test_usage.py::test_upload01_upload_a_file PASSED [ 37%]
tests/test_disabled.py::test_disabled01_check_disabled_property_update PASSED [ 50%]
tests/test_disabled.py::test_disabled02_check_disabled_effect PASSED [ 62%]
tests/test_uploading_same_file_twice_with_errors.py::test_uploadtwice01_upload_a_file_twice_and_reserve_it PASSED [ 75%]
tests/test_uploading_same_file_twice_with_errors.py::test_uploadtwice02_upload_a_file_twice_with_error FAILED [ 87%]
tests/test_uploadstatus.py::test_uploadstatus_creation_and_serialization PASSED [100%]
======================================================================================================= FAILURES ========================================================================================================
___________________________________________________________________________________ test_uploadtwice02_upload_a_file_twice_with_error ___________________________________________________________________________________
dash_duo = <dash.testing.composite.DashComposite object at 0x10775ca30>, testfile10kb_csv = PosixPath('/Users/wfluo/workspaces/dash-uploader/tests/testfile_for_reupload.csv')
testfile10kb_2_csv = PosixPath('/Users/wfluo/workspaces/dash-uploader/tests/testfile2_for_reupload.csv')
def test_uploadtwice02_upload_a_file_twice_with_error(
dash_duo, testfile10kb_csv, testfile10kb_2_csv
):
# Same as test_uploadtwice01_upload_a_file_twice_and_reserve_it
# but force an error
# This app does not have retries on the "remove_file"
# function, and therefore the error alert will appear
# to the user instantly. (to make tests faster)
app = import_app("tests.apps.testapp_noretry_remove_file")
dash_duo.start_server(app)
def upload_file(file_to_upload):
# User sees the component
upload = dash_duo.find_element("#dash-uploader")
# Upload the file.
# Clicking the upload component would open a file dialog and
# this would require the tests to use OS specific GUI tools
# to select the file. This could be added in the future but it's
# probably very this would be broken
upload_input = upload.find_element(
By.XPATH, "//input[@name='dash-uploader-upload']"
)
upload_input.send_keys(str(file_to_upload))
upload_file(testfile10kb_csv)
# Wait for "Completed: testfile_for_reupload.csv" text, with 10 second timeout
WebDriverWait(dash_duo._driver, 10).until(
EC.text_to_be_present_in_element(
(By.XPATH, "//div[@id='dash-uploader']/*/label"), testfile10kb_csv.name
)
)
# Get the div with the output values
callback_output = dash_duo.find_element("#callback-output")
# Get the name of the uploaded file
uploaded_file = callback_output.find_element(By.XPATH, "//ul").text
uploaded_file = Path(uploaded_file)
assert uploaded_file.name == testfile10kb_csv.name
assert uploaded_file.exists()
assert uploaded_file.stat().st_size == testfile10kb_csv.stat().st_size
# Upload another file to change the labels.
upload_file(testfile10kb_2_csv)
# Wait for "Completed: testfile2_for_reupload.csv" text, with 10 second timeout
WebDriverWait(dash_duo._driver, 10).until(
EC.text_to_be_present_in_element(
(By.XPATH, "//div[@id='dash-uploader']/*/label"), testfile10kb_2_csv.name
)
)
# Wait that the callback for the 'testfile2_for_reupload.csv' has been fired.
WebDriverWait(dash_duo._driver, 10).until(
EC.text_to_be_present_in_element(
(By.XPATH, '//*[@id="callback-output"]/ul/li'), testfile10kb_2_csv.name
)
)
# Reserve file & make it impossible to upload testfile10kb_csv
# Hold the file for 4 seconds
file_reserve_thread = threading.Thread(
target=reserve_file_for_while, args=(uploaded_file, HOLD_TIME_FOR_FILE)
)
file_reserve_thread.start()
# Reupload file again with same filename
upload_file(testfile10kb_csv)
# Expect to see an error alert text for the user
# in the following seconds
> WebDriverWait(dash_duo._driver, 4).until(EC.alert_is_present())
tests/test_uploading_same_file_twice_with_errors.py:214:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <selenium.webdriver.support.wait.WebDriverWait (session="b1af585958442773e1def274d4b51b67")>, method = <function alert_is_present.<locals>._predicate at 0x107780160>, message = ''
def until(self, method, message=''):
"""Calls the method provided with the driver as an argument until the \
return value does not evaluate to ``False``.
:param method: callable(WebDriver)
:param message: optional message for :exc:`TimeoutException`
:returns: the result of the last call to `method`
:raises: :exc:`selenium.common.exceptions.TimeoutException` if timeout occurs
"""
screen = None
stacktrace = None
end_time = time.monotonic() + self._timeout
while True:
try:
value = method(self._driver)
if value:
return value
except self._ignored_exceptions as exc:
screen = getattr(exc, 'screen', None)
stacktrace = getattr(exc, 'stacktrace', None)
time.sleep(self._poll)
if time.monotonic() > end_time:
break
> raise TimeoutException(message, screen, stacktrace)
E selenium.common.exceptions.TimeoutException: Message:
../../miniconda3/envs/dash/lib/python3.10/site-packages/selenium/webdriver/support/wait.py:87: TimeoutException
------------------------------------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------------------------------------
Dash is running on http://127.0.0.1:58055/
* Serving Flask app 'tests.apps.testapp_noretry_remove_file'
* Debug mode: off
<UploadStatus: latest_file = C:\tmp\Uploads/680cec64-8cf4-11ee-984b-bef0c687ed75/testfile_for_reupload.csv, uploaded_files = [C:\tmp\Uploads/680cec64-8cf4-11ee-984b-bef0c687ed75/testfile_for_reupload.csv], is_completed = True, n_uploaded = 1, n_total = 1, uploaded_size_mb = 0.009766578674316406, total_size_mb = 0.009766578674316406, progress = 1.0, upload_id = 680cec64-8cf4-11ee-984b-bef0c687ed75>
<UploadStatus: latest_file = C:\tmp\Uploads/680cec64-8cf4-11ee-984b-bef0c687ed75/testfile2_for_reupload.csv, uploaded_files = [C:\tmp\Uploads/680cec64-8cf4-11ee-984b-bef0c687ed75/testfile2_for_reupload.csv], is_completed = True, n_uploaded = 1, n_total = 1, uploaded_size_mb = 0.009766578674316406, total_size_mb = 0.009766578674316406, progress = 1.0, upload_id = 680cec64-8cf4-11ee-984b-bef0c687ed75>
<UploadStatus: latest_file = C:\tmp\Uploads/680cec64-8cf4-11ee-984b-bef0c687ed75/testfile_for_reupload.csv, uploaded_files = [C:\tmp\Uploads/680cec64-8cf4-11ee-984b-bef0c687ed75/testfile_for_reupload.csv], is_completed = True, n_uploaded = 1, n_total = 1, uploaded_size_mb = 0.009766578674316406, total_size_mb = 0.009766578674316406, progress = 1.0, upload_id = 680cec64-8cf4-11ee-984b-bef0c687ed75>
--------------------------------------------------------------------------------------------------- Captured log call ---------------------------------------------------------------------------------------------------
15:13:03 | INFO | dash.dash:2040 | Dash is running on http://127.0.0.1:58055/
15:13:03 | INFO | werkzeug:224 | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:58055
15:13:03 | INFO | werkzeug:224 | Press CTRL+C to quit
15:13:03 | INFO | werkzeug:224 | 127.0.0.1 - - [27/Nov/2023 15:13:03] "GET / HTTP/1.1" 200 -
15:13:03 | INFO | werkzeug:224 | 127.0.0.1 - - [27/Nov/2023 15:13:03] "GET / HTTP/1.1" 200 -
15:13:03 | INFO | werkzeug:224 | 127.0.0.1 - - [27/Nov/2023 15:13:03] "GET /_dash-component-suites/dash/deps/polyfill@7.v2_13_0m1697123470.12.1.min.js HTTP/1.1" 200 -
15:13:03 | INFO | werkzeug:224 | 127.0.0.1 - - [27/Nov/2023 15:13:03] "GET /_dash-component-suites/dash/deps/react-dom@16.v2_13_0m1697123470.14.0.min.js HTTP/1.1" 200 -
15:13:03 | INFO | werkzeug:224 | 127.0.0.1 - - [27/Nov/2023 15:13:03] "GET /_dash-component-suites/dash/deps/prop-types@15.v2_13_0m1697123470.8.1.min.js HTTP/1.1" 200 -
15:13:03 | INFO | werkzeug:224 | 127.0.0.1 - - [27/Nov/2023 15:13:03] "GET /_dash-component-suites/dash_uploader/_build/dash_uploader.v0_7_0-a2m1698038156.min.js HTTP/1.1" 200 -
15:13:03 | INFO | werkzeug:224 | 127.0.0.1 - - [27/Nov/2023 15:13:03] "GET /_dash-component-suites/dash/deps/react@16.v2_13_0m1697123470.14.0.min.js HTTP/1.1" 200 -
15:13:03 | INFO | werkzeug:224 | 127.0.0.1 - - [27/Nov/2023 15:13:03] "GET /_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_13_0m1697123469.min.js HTTP/1.1" 200 -
15:13:03 | INFO | werkzeug:224 | 127.0.0.1 - - [27/Nov/2023 15:13:03] "GET /_dash-component-suites/dash/dcc/dash_core_components.v2_12_0m1697123469.js HTTP/1.1" 200 -
15:13:03 | INFO | werkzeug:224 | 127.0.0.1 - - [27/Nov/2023 15:13:03] "GET /_dash-component-suites/dash/dcc/dash_core_components-shared.v2_12_0m1697123469.js HTTP/1.1" 200 -
15:13:03 | INFO | werkzeug:224 | 127.0.0.1 - - [27/Nov/2023 15:13:03] "GET /_dash-component-suites/dash/html/dash_html_components.v2_0_14m1697123470.min.js HTTP/1.1" 200 -
15:13:03 | INFO | werkzeug:224 | 127.0.0.1 - - [27/Nov/2023 15:13:03] "GET /_dash-component-suites/dash/dash_table/bundle.v5_2_7m1697123469.js HTTP/1.1" 200 -
15:13:04 | INFO | werkzeug:224 | 127.0.0.1 - - [27/Nov/2023 15:13:04] "GET /_dash-layout HTTP/1.1" 200 -
15:13:04 | INFO | werkzeug:224 | 127.0.0.1 - - [27/Nov/2023 15:13:04] "GET /_dash-dependencies HTTP/1.1" 200 -
15:13:04 | INFO | werkzeug:224 | 127.0.0.1 - - [27/Nov/2023 15:13:04] "GET /_favicon.ico?v=2.13.0 HTTP/1.1" 200 -
15:13:04 | INFO | werkzeug:224 | 127.0.0.1 - - [27/Nov/2023 15:13:04] "POST /API/dash-uploader HTTP/1.1" 200 -
15:13:04 | INFO | werkzeug:224 | 127.0.0.1 - - [27/Nov/2023 15:13:04] "POST /_dash-update-component HTTP/1.1" 200 -
15:13:04 | INFO | werkzeug:224 | 127.0.0.1 - - [27/Nov/2023 15:13:04] "POST /_dash-update-component HTTP/1.1" 204 -
15:13:04 | INFO | werkzeug:224 | 127.0.0.1 - - [27/Nov/2023 15:13:04] "POST /API/dash-uploader HTTP/1.1" 200 -
15:13:04 | INFO | werkzeug:224 | 127.0.0.1 - - [27/Nov/2023 15:13:04] "POST /_dash-update-component HTTP/1.1" 200 -
15:13:04 | INFO | werkzeug:224 | 127.0.0.1 - - [27/Nov/2023 15:13:04] "POST /_dash-update-component HTTP/1.1" 204 -
15:13:04 | INFO | werkzeug:224 | 127.0.0.1 - - [27/Nov/2023 15:13:04] "POST /API/dash-uploader HTTP/1.1" 200 -
15:13:04 | INFO | werkzeug:224 | 127.0.0.1 - - [27/Nov/2023 15:13:04] "POST /_dash-update-component HTTP/1.1" 200 -
=================================================================================================== warnings summary ====================================================================================================
tests/test_chromedriver.py::test_chromedriver_version_okay
/Users/wfluo/miniconda3/envs/dash/lib/python3.10/site-packages/selenium/webdriver/remote/remote_connection.py:390: DeprecationWarning: HTTPResponse.getheader() is deprecated and will be removed in urllib3 v2.1.0. Instead use HTTPResponse.headers.get(name, default).
if response.getheader('Content-Type'):
tests/test_chromedriver.py::test_chromedriver_version_okay
/Users/wfluo/miniconda3/envs/dash/lib/python3.10/site-packages/selenium/webdriver/remote/remote_connection.py:391: DeprecationWarning: HTTPResponse.getheader() is deprecated and will be removed in urllib3 v2.1.0. Instead use HTTPResponse.headers.get(name, default).
content_type = response.getheader('Content-Type').split(';')
tests/test_usage.py: 25 warnings
tests/test_disabled.py: 175 warnings
tests/test_uploading_same_file_twice_with_errors.py: 56 warnings
/Users/wfluo/miniconda3/envs/dash/lib/python3.10/site-packages/selenium/webdriver/remote/remote_connection.py:390: DeprecationWarning:
HTTPResponse.getheader() is deprecated and will be removed in urllib3 v2.1.0. Instead use HTTPResponse.headers.get(name, default).
tests/test_usage.py: 25 warnings
tests/test_disabled.py: 175 warnings
tests/test_uploading_same_file_twice_with_errors.py: 56 warnings
/Users/wfluo/miniconda3/envs/dash/lib/python3.10/site-packages/selenium/webdriver/remote/remote_connection.py:391: DeprecationWarning:
HTTPResponse.getheader() is deprecated and will be removed in urllib3 v2.1.0. Instead use HTTPResponse.headers.get(name, default).
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
====================================================================================== 1 failed, 7 passed, 514 warnings in 47.66s =======================================================================================
state
parameter todu.callback
support more state supplies such as token or current sessionRef: https://github.com/fohrloop/dash-uploader/issues/104