From 44262e3ebf33cfb64998d7d0e95eec03d3beabf7 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Sat, 16 May 2020 00:33:45 -0400 Subject: [PATCH 1/4] hard reload targets just this window --- dash-renderer/src/components/core/Reloader.react.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dash-renderer/src/components/core/Reloader.react.js b/dash-renderer/src/components/core/Reloader.react.js index f91df5c415..0a20dd9edd 100644 --- a/dash-renderer/src/components/core/Reloader.react.js +++ b/dash-renderer/src/components/core/Reloader.react.js @@ -148,7 +148,7 @@ class Reloader extends React.Component { // Assets file have changed // or a component lib has been added/removed - // Must do a hard reload - window.top.location.reload(); + window.location.reload(); } } else { // Backend code changed - can do a soft reload in place From 6747fd4a22065ec7fc734519d6acaf3acfbfdadb Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Sat, 16 May 2020 01:13:59 -0400 Subject: [PATCH 2/4] hard & soft reload test --- .../devtools/hr_assets/hot_reload.js | 2 + tests/integration/devtools/test_hot_reload.py | 63 +++++++++++++++---- 2 files changed, 54 insertions(+), 11 deletions(-) create mode 100644 tests/integration/devtools/hr_assets/hot_reload.js diff --git a/tests/integration/devtools/hr_assets/hot_reload.js b/tests/integration/devtools/hr_assets/hot_reload.js new file mode 100644 index 0000000000..71acdc2219 --- /dev/null +++ b/tests/integration/devtools/hr_assets/hot_reload.js @@ -0,0 +1,2 @@ + +window.cheese = 'roquefort'; diff --git a/tests/integration/devtools/test_hot_reload.py b/tests/integration/devtools/test_hot_reload.py index 5fdaa50865..83669ba6c3 100644 --- a/tests/integration/devtools/test_hot_reload.py +++ b/tests/integration/devtools/test_hot_reload.py @@ -1,6 +1,7 @@ import os from time import sleep +from dash.testing.wait import until import dash_html_components as html import dash from dash.dependencies import Input, Output @@ -13,6 +14,24 @@ } """ +GOUDA = """ +window.cheese = 'gouda'; +""" + + +def replace_file(filename, new_content): + path = os.path.join( + os.path.dirname(__file__), "hr_assets", filename + ) + with open(path, "r+") as fp: + sleep(1) # ensure a new mod time + old_content = fp.read() + fp.truncate(0) + fp.seek(0) + fp.write(new_content) + + return path, old_content + def test_dvhr001_hot_reload(dash_duo): app = dash.Dash(__name__, assets_folder="hr_assets") @@ -42,15 +61,12 @@ def new_text(n): "#hot-reload-content", "background-color", "rgba(0, 0, 255, 1)" ) - hot_reload_file = os.path.join( - os.path.dirname(__file__), "hr_assets", "hot_reload.css" - ) - with open(hot_reload_file, "r+") as fp: - sleep(1) # ensure a new mod time - old_content = fp.read() - fp.truncate(0) - fp.seek(0) - fp.write(RED_BG) + # set a global var - if we soft reload it should still be there, + # hard reload will delete it + dash_duo.driver.execute_script("window.someVar = 42;") + assert dash_duo.driver.execute_script("return window.someVar") == 42 + + soft_reload_file, old_soft = replace_file("hot_reload.css", RED_BG) try: # red is live changed during the test execution @@ -59,13 +75,38 @@ def new_text(n): ) finally: sleep(1) # ensure a new mod time - with open(hot_reload_file, "w") as f: - f.write(old_content) + with open(soft_reload_file, "w") as f: + f.write(old_soft) dash_duo.wait_for_style_to_equal( "#hot-reload-content", "background-color", "rgba(0, 0, 255, 1)" ) + # only soft reload, someVar is still there + assert dash_duo.driver.execute_script("return window.someVar") == 42 + + assert dash_duo.driver.execute_script("return window.cheese") == "roquefort" + + hard_reload_file, old_hard = replace_file("hot_reload.js", GOUDA) + + try: + until( + lambda: dash_duo.driver.execute_script("return window.cheese") == "gouda", + timeout=3 + ) + finally: + sleep(1) # ensure a new mod time + with open(hard_reload_file, "w") as f: + f.write(old_hard) + + until( + lambda: dash_duo.driver.execute_script("return window.cheese") == "roquefort", + timeout=3 + ) + + # we've done a hard reload so someVar is gone + assert dash_duo.driver.execute_script("return window.someVar") is None + # Now check the server status indicator functionality dash_duo.find_element(".dash-debug-menu").click() From fc83610a1621822dcd165a451597c3a98329df35 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Sat, 16 May 2020 01:16:10 -0400 Subject: [PATCH 3/4] changelog for hard hot reload fix --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index de8d84ce38..b5bd28ee4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - [#1237](https://github.com/plotly/dash/pull/1237) Closes [#920](https://github.com/plotly/dash/issues/920): Converts hot reload fetch failures into a server status indicator showing whether the latest fetch succeeded or failed. Callback fetch failures still appear as errors but have a clearer message. ### Fixed +- [#1255](https://github.com/plotly/dash/pull/1255) Hard hot reload targets only the current window, not the top - so if your app is in an iframe you will only reload the app - [#1249](https://github.com/plotly/dash/pull/1249) Fixes [#919](https://github.com/plotly/dash/issues/919) so `dash.testing` is compatible with more `pytest` plugins, particularly `pytest-flake8` and `pytest-black`. - [#1248](https://github.com/plotly/dash/pull/1248) Fixes [#1245](https://github.com/plotly/dash/issues/1245), so you can use prop persistence with components that have dict IDs, ie for pattern-matching callbacks. - [#1185](https://github.com/plotly/dash/pull/1185) Sort asset directories, same as we sort files inside those directories. This way if you need your assets loaded in a certain order, you can add prefixes to subdirectory names and enforce that order. From 3309c814d0137230cbb4f78cba749f6127a295c2 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Wed, 20 May 2020 10:26:43 -0400 Subject: [PATCH 4/4] robustify rddd001 --- tests/integration/renderer/test_due_diligence.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration/renderer/test_due_diligence.py b/tests/integration/renderer/test_due_diligence.py index cb44d39fb0..3ec1ce40ba 100644 --- a/tests/integration/renderer/test_due_diligence.py +++ b/tests/integration/renderer/test_due_diligence.py @@ -54,6 +54,7 @@ def test_rddd001_initial_state(dash_duo): # fmt:on dash_duo.start_server(app) + dash_duo.wait_for_text_to_equal(r"#p\.c\.5", "") # Note: this .html file shows there's no undo/redo button by default with open(