|
| 1 | +from multiprocessing import Lock |
| 2 | +from selenium.webdriver.common.action_chains import ActionChains |
| 3 | +from dash.testing.wait import until |
| 4 | + |
| 5 | +from dash import Dash, Input, Output, dcc, html, no_update |
| 6 | + |
| 7 | + |
| 8 | +def test_ttbs001_canonical_behavior(dash_dcc): |
| 9 | + lock = Lock() |
| 10 | + |
| 11 | + loading_text = "Waiting for Godot" |
| 12 | + |
| 13 | + fig = dict( |
| 14 | + data=[ |
| 15 | + dict( |
| 16 | + x=[11, 22, 33], y=[333, 222, 111], mode="markers", marker=dict(size=40) |
| 17 | + ) |
| 18 | + ], |
| 19 | + layout=dict(width=400, height=400, margin=dict(l=100, r=100, t=100, b=100)), |
| 20 | + ) |
| 21 | + app = Dash(__name__) |
| 22 | + |
| 23 | + app.layout = html.Div( |
| 24 | + className="container", |
| 25 | + children=[ |
| 26 | + dcc.Graph(id="graph", figure=fig, clear_on_unhover=True), |
| 27 | + dcc.Tooltip(id="graph-tooltip", loading_text=loading_text), |
| 28 | + ], |
| 29 | + style=dict(position="relative"), |
| 30 | + ) |
| 31 | + |
| 32 | + # This callback is executed very quickly |
| 33 | + app.clientside_callback( |
| 34 | + """ |
| 35 | + function show_tooltip(hoverData) { |
| 36 | + if(!hoverData) { |
| 37 | + return [false, dash_clientside.no_update]; |
| 38 | + } |
| 39 | + var pt = hoverData.points[0]; |
| 40 | + return [true, pt.bbox]; |
| 41 | + } |
| 42 | + """, |
| 43 | + Output("graph-tooltip", "show"), |
| 44 | + Output("graph-tooltip", "bbox"), |
| 45 | + Input("graph", "hoverData"), |
| 46 | + ) |
| 47 | + |
| 48 | + # This callback is executed after 1s to simulate a long-running process |
| 49 | + @app.callback( |
| 50 | + Output("graph-tooltip", "children"), |
| 51 | + Input("graph", "hoverData"), |
| 52 | + ) |
| 53 | + def update_tooltip_content(hoverData): |
| 54 | + if hoverData is None: |
| 55 | + return no_update |
| 56 | + |
| 57 | + with lock: |
| 58 | + # Display the x0 and y0 coordinate |
| 59 | + bbox = hoverData["points"][0]["bbox"] |
| 60 | + return [ |
| 61 | + html.P(f"x0={bbox['x0']}, y0={bbox['y0']}"), |
| 62 | + ] |
| 63 | + |
| 64 | + dash_dcc.start_server(app) |
| 65 | + |
| 66 | + until(lambda: not dash_dcc.find_element("#graph-tooltip").is_displayed(), 3) |
| 67 | + |
| 68 | + elem = dash_dcc.find_element("#graph .nsewdrag") |
| 69 | + |
| 70 | + with lock: |
| 71 | + # hover on the center of the graph |
| 72 | + ActionChains(dash_dcc.driver).move_to_element_with_offset( |
| 73 | + elem, elem.size["width"] / 2, elem.size["height"] / 2 |
| 74 | + ).click().perform() |
| 75 | + dash_dcc.wait_for_text_to_equal("#graph-tooltip", loading_text) |
| 76 | + |
| 77 | + dash_dcc.wait_for_contains_text("#graph-tooltip", "x0=") |
| 78 | + tt_text = dash_dcc.find_element("#graph-tooltip").text |
| 79 | + coords = [float(part.split("=")[1]) for part in tt_text.split(",")] |
| 80 | + assert 175 < coords[0] < 180, "x0 is about 200 minus half a marker size" |
| 81 | + assert 175 < coords[1] < 185, "y0 is about 200 minus half a marker size" |
| 82 | + |
| 83 | + ActionChains(dash_dcc.driver).move_to_element_with_offset(elem, 0, 0).perform() |
| 84 | + |
| 85 | + until(lambda: not dash_dcc.find_element("#graph-tooltip").is_displayed(), 3) |
0 commit comments