|
5 | 5 |
|
6 | 6 | import pytest
|
7 | 7 | import time
|
| 8 | +import multiprocessing |
| 9 | + |
8 | 10 | import numpy as np
|
9 | 11 | import pandas as pd
|
10 |
| -import multiprocessing |
11 | 12 | import plotly.graph_objects as go
|
| 13 | + |
| 14 | +from selenium.webdriver.common.by import By |
| 15 | +from typing import List |
| 16 | + |
12 | 17 | from plotly.subplots import make_subplots
|
13 | 18 | from plotly_resampler import FigureResampler, LTTB, EveryNthPoint
|
14 |
| -from typing import List |
| 19 | + |
| 20 | +# Note: this will be used to skip / alter behavior when running browser tests on |
| 21 | +# non-linux platforms. |
| 22 | +from .utils import not_on_linux |
15 | 23 |
|
16 | 24 |
|
17 | 25 | def test_add_trace_kwarg_space(float_series, bool_series, cat_series):
|
@@ -1027,6 +1035,184 @@ def test_fr_object_binary_data():
|
1027 | 1035 | assert np.all(fig.data[0]["y"] == binary_series)
|
1028 | 1036 |
|
1029 | 1037 |
|
| 1038 | +def test_fr_update_layout_axes_range(driver): |
| 1039 | + nb_datapoints = 2_000 |
| 1040 | + n_shown = 500 # < nb_datapoints |
| 1041 | + |
| 1042 | + # Checks whether the update_layout method works as expected |
| 1043 | + f_orig = go.Figure().add_scatter(y=np.arange(nb_datapoints)) |
| 1044 | + f_pr = FigureResampler(default_n_shown_samples=n_shown).add_scatter( |
| 1045 | + y=np.arange(nb_datapoints) |
| 1046 | + ) |
| 1047 | + |
| 1048 | + def check_data(fr: FigureResampler, min_v=0, max_v=nb_datapoints-1): |
| 1049 | + # closure for n_shown and nb_datapoints |
| 1050 | + assert len(fr.data[0]["y"]) == min(n_shown, nb_datapoints) |
| 1051 | + assert len(fr.data[0]["x"]) == min(n_shown, nb_datapoints) |
| 1052 | + assert fr.data[0]["y"][0] == min_v |
| 1053 | + assert fr.data[0]["y"][-1] == max_v |
| 1054 | + assert fr.data[0]["x"][0] == min_v |
| 1055 | + assert fr.data[0]["x"][-1] == max_v |
| 1056 | + |
| 1057 | + # Check the initial data |
| 1058 | + check_data(f_pr) |
| 1059 | + |
| 1060 | + # The xaxis (auto)range should be the same for both figures |
| 1061 | + |
| 1062 | + assert f_orig.layout.xaxis.range == None |
| 1063 | + assert f_pr.layout.xaxis.range == None |
| 1064 | + assert f_orig.layout.xaxis.autorange == None |
| 1065 | + assert f_pr.layout.xaxis.autorange == None |
| 1066 | + |
| 1067 | + f_orig.update_layout(xaxis_range=[100, 1000]) |
| 1068 | + f_pr.update_layout(xaxis_range=[100, 1000]) |
| 1069 | + |
| 1070 | + assert f_orig.layout.xaxis.range == (100, 1000) |
| 1071 | + assert f_pr.layout.xaxis.range == (100, 1000) |
| 1072 | + assert f_orig.layout.xaxis.autorange == None |
| 1073 | + assert f_pr.layout.xaxis.autorange == None |
| 1074 | + |
| 1075 | + # The yaxis (auto)range should be the same for both figures |
| 1076 | + |
| 1077 | + assert f_orig.layout.yaxis.range == None |
| 1078 | + assert f_pr.layout.yaxis.range == None |
| 1079 | + assert f_orig.layout.yaxis.autorange == None |
| 1080 | + assert f_pr.layout.yaxis.autorange == None |
| 1081 | + |
| 1082 | + f_orig.update_layout(yaxis_range=[100, 1000]) |
| 1083 | + f_pr.update_layout(yaxis_range=[100, 1000]) |
| 1084 | + |
| 1085 | + assert list(f_orig.layout.yaxis.range) == [100, 1000] |
| 1086 | + assert list(f_pr.layout.yaxis.range) == [100, 1000] |
| 1087 | + assert f_orig.layout.yaxis.autorange == None |
| 1088 | + assert f_pr.layout.yaxis.autorange == None |
| 1089 | + |
| 1090 | + # Before showing the figure, the f_pr contains the full original data (downsampled to 500 samples) |
| 1091 | + # Even after updating the axes ranges |
| 1092 | + check_data(f_pr) |
| 1093 | + |
| 1094 | + if not_on_linux(): |
| 1095 | + # TODO: eventually we should run this test on Windows & MacOS too |
| 1096 | + return |
| 1097 | + |
| 1098 | + f_pr.stop_server() |
| 1099 | + proc = multiprocessing.Process(target=f_pr.show_dash, kwargs=dict(mode="external")) |
| 1100 | + proc.start() |
| 1101 | + try: |
| 1102 | + time.sleep(1) |
| 1103 | + driver.get(f"http://localhost:8050") |
| 1104 | + time.sleep(3) |
| 1105 | + # Get the data property from the front-end figure |
| 1106 | + el = driver.find_element(by=By.ID, value="resample-figure") |
| 1107 | + el = el.find_element(by=By.CLASS_NAME, value="js-plotly-plot") |
| 1108 | + f_pr_data = el.get_property("data") |
| 1109 | + f_pr_layout = el.get_property("layout") |
| 1110 | + |
| 1111 | + # After showing the figure, the f_pr contains the data of the selected xrange (downsampled to 500 samples) |
| 1112 | + assert len(f_pr_data[0]["y"]) == 500 |
| 1113 | + assert len(f_pr_data[0]["x"]) == 500 |
| 1114 | + assert f_pr_data[0]["y"][0] >= 100 and f_pr_data[0]["y"][-1] <= 1000 |
| 1115 | + assert f_pr_data[0]["x"][0] >= 100 and f_pr_data[0]["x"][-1] <= 1000 |
| 1116 | + # Check the front-end layout |
| 1117 | + assert list(f_pr_layout["xaxis"]["range"]) == [100, 1000] |
| 1118 | + assert list(f_pr_layout["yaxis"]["range"]) == [100, 1000] |
| 1119 | + except Exception as e: |
| 1120 | + raise e |
| 1121 | + finally: |
| 1122 | + proc.terminate() |
| 1123 | + f_pr.stop_server() |
| 1124 | + |
| 1125 | + |
| 1126 | +def test_fr_update_layout_axes_range_no_update(driver): |
| 1127 | + nb_datapoints = 2_000 |
| 1128 | + n_shown = 20_000 # > nb. datapoints |
| 1129 | + |
| 1130 | + # Checks whether the update_layout method works as expected |
| 1131 | + f_orig = go.Figure().add_scatter(y=np.arange(nb_datapoints)) |
| 1132 | + f_pr = FigureResampler(default_n_shown_samples=n_shown).add_scatter( |
| 1133 | + y=np.arange(nb_datapoints) |
| 1134 | + ) |
| 1135 | + |
| 1136 | + def check_data(fr: FigureResampler, min_v=0, max_v=nb_datapoints-1): |
| 1137 | + # closure for n_shown and nb_datapoints |
| 1138 | + assert len(fr.data[0]["y"]) == min(n_shown, nb_datapoints) |
| 1139 | + assert len(fr.data[0]["x"]) == min(n_shown, nb_datapoints) |
| 1140 | + assert fr.data[0]["y"][0] == min_v |
| 1141 | + assert fr.data[0]["y"][-1] == max_v |
| 1142 | + assert fr.data[0]["x"][0] == min_v |
| 1143 | + assert fr.data[0]["x"][-1] == max_v |
| 1144 | + |
| 1145 | + # Check the initial data |
| 1146 | + check_data(f_pr) |
| 1147 | + |
| 1148 | + # The xaxis (auto)range should be the same for both figures |
| 1149 | + |
| 1150 | + assert f_orig.layout.xaxis.range == None |
| 1151 | + assert f_pr.layout.xaxis.range == None |
| 1152 | + assert f_orig.layout.xaxis.autorange == None |
| 1153 | + assert f_pr.layout.xaxis.autorange == None |
| 1154 | + |
| 1155 | + f_orig.update_layout(xaxis_range=[100, 1000]) |
| 1156 | + f_pr.update_layout(xaxis_range=[100, 1000]) |
| 1157 | + |
| 1158 | + assert f_orig.layout.xaxis.range == (100, 1000) |
| 1159 | + assert f_pr.layout.xaxis.range == (100, 1000) |
| 1160 | + assert f_orig.layout.xaxis.autorange == None |
| 1161 | + assert f_pr.layout.xaxis.autorange == None |
| 1162 | + |
| 1163 | + # The yaxis (auto)range should be the same for both figures |
| 1164 | + |
| 1165 | + assert f_orig.layout.yaxis.range == None |
| 1166 | + assert f_pr.layout.yaxis.range == None |
| 1167 | + assert f_orig.layout.yaxis.autorange == None |
| 1168 | + assert f_pr.layout.yaxis.autorange == None |
| 1169 | + |
| 1170 | + f_orig.update_layout(yaxis_range=[100, 1000]) |
| 1171 | + f_pr.update_layout(yaxis_range=[100, 1000]) |
| 1172 | + |
| 1173 | + assert list(f_orig.layout.yaxis.range) == [100, 1000] |
| 1174 | + assert list(f_pr.layout.yaxis.range) == [100, 1000] |
| 1175 | + assert f_orig.layout.yaxis.autorange == None |
| 1176 | + assert f_pr.layout.yaxis.autorange == None |
| 1177 | + |
| 1178 | + # Before showing the figure, the f_pr contains the full original data (not downsampled) |
| 1179 | + # Even after updating the axes ranges |
| 1180 | + check_data(f_pr) |
| 1181 | + |
| 1182 | + if not_on_linux(): |
| 1183 | + # TODO: eventually we should run this test on Windows & MacOS too |
| 1184 | + return |
| 1185 | + |
| 1186 | + f_pr.stop_server() |
| 1187 | + proc = multiprocessing.Process(target=f_pr.show_dash, kwargs=dict(mode="external")) |
| 1188 | + proc.start() |
| 1189 | + try: |
| 1190 | + time.sleep(1) |
| 1191 | + driver.get(f"http://localhost:8050") |
| 1192 | + time.sleep(3) |
| 1193 | + # Get the data & layout property from the front-end figure |
| 1194 | + el = driver.find_element(by=By.ID, value="resample-figure") |
| 1195 | + el = el.find_element(by=By.CLASS_NAME, value="js-plotly-plot") |
| 1196 | + f_pr_data = el.get_property("data") |
| 1197 | + f_pr_layout = el.get_property("layout") |
| 1198 | + |
| 1199 | + # After showing the figure, the f_pr contains the original data (not downsampled), but shown xrange is [100, 1000] |
| 1200 | + assert len(f_pr_data[0]["y"]) == 2_000 |
| 1201 | + assert len(f_pr_data[0]["x"]) == 2_000 |
| 1202 | + assert f_pr.data[0]["y"][0] == 0 |
| 1203 | + assert f_pr.data[0]["y"][-1] == 1999 |
| 1204 | + assert f_pr.data[0]["x"][0] == 0 |
| 1205 | + assert f_pr.data[0]["x"][-1] == 1999 |
| 1206 | + # Check the front-end layout |
| 1207 | + assert list(f_pr_layout["xaxis"]["range"]) == [100, 1000] |
| 1208 | + assert list(f_pr_layout["yaxis"]["range"]) == [100, 1000] |
| 1209 | + except Exception as e: |
| 1210 | + raise e |
| 1211 | + finally: |
| 1212 | + proc.terminate() |
| 1213 | + f_pr.stop_server() |
| 1214 | + |
| 1215 | + |
1030 | 1216 | def test_fr_copy_grid():
|
1031 | 1217 | # Checks whether _grid_ref and _grid_str are correctly maintained
|
1032 | 1218 |
|
|
0 commit comments