Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #1413: Update and split dash_duo tests #1417

Merged
merged 11 commits into from
Jul 19, 2024
84 changes: 84 additions & 0 deletions pdr_backend/analytics/predictoor_dashboard/test/conftest.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,31 @@
import pytest
import dash_bootstrap_components as dbc

from dash import Dash
from selenium.webdriver.chrome.options import Options

from pdr_backend.lake.payout import mock_payouts, mock_payouts_related_with_predictions
from pdr_backend.lake.prediction import mock_daily_predictions, mock_first_predictions

from pdr_backend.analytics.predictoor_dashboard.dash_components.callbacks import (
get_callbacks,
)
from pdr_backend.analytics.predictoor_dashboard.dash_components.view_elements import (
get_layout,
)
from pdr_backend.analytics.predictoor_dashboard.test.resources import (
_prepare_test_db,
_clear_test_db,
)
from pdr_backend.lake.payout import Payout
from pdr_backend.lake.prediction import Prediction

from pdr_backend.analytics.predictoor_dashboard.dash_components.util import (
get_feeds_data_from_db,
get_predictoors_data_from_payouts,
get_user_payouts_stats_from_db,
)


@pytest.fixture()
def _sample_first_predictions():
Expand Down Expand Up @@ -42,3 +64,65 @@ def pytest_setup_options():
options.add_argument("--headless")
options.add_argument("--disable-gpu")
return options


@pytest.fixture
def setup_app(
tmpdir, _sample_daily_predictions, _sample_payouts_related_with_predictions
):
_clear_test_db(str(tmpdir))

_prepare_test_db(
tmpdir,
_sample_payouts_related_with_predictions,
table_name=Payout.get_lake_table_name(),
)

ppss, _ = _prepare_test_db(
tmpdir,
_sample_daily_predictions,
table_name=Prediction.get_lake_table_name(),
)

app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.config["suppress_callback_exceptions"] = True

style_css = ""
# read the styles.css file and add it to the assets folder
# Read the styles.css file
with open("pdr_backend/analytics/predictoor_dashboard/assets/styles.css", "r") as f:
style_css = f.read()

# Manually link the CSS file by embedding its contents in a <style> tag
app.index_string = f"""
<!DOCTYPE html>
<html>
<head>
{{%metas%}}
<title>{{%title%}}</title>
{{%favicon%}}
{{%css%}}
<style>{style_css}</style>
</head>
<body>
{{%app_entry%}}
<footer>
{{%config%}}
{{%scripts%}}
{{%renderer%}}
</footer>
</body>
</html>
"""

app.layout = get_layout()
app.lake_dir = ppss.lake_ss.lake_dir
app.feeds_data = get_feeds_data_from_db(ppss.lake_ss.lake_dir)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to avoid possible inconsistencies with this part, let's create a function that takes app and ppss as parameters, called e.g. setup_predictoor_dashboard and add predictoors_data, feeds_data, lake_dir, favourite_addresses. Call this function here and where this code is duplicated. I wanted to do this myself at the last round of fixes but I missed it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

other dashboards have at most one line of setup, so that's why we just copy that setup in the tests, but here I think it's safer to do a function

app.predictoors_data = get_predictoors_data_from_payouts(
get_user_payouts_stats_from_db(ppss.lake_ss.lake_dir)
)
app.favourite_addresses = ppss.predictoor_ss.my_addresses
get_callbacks(app)
app.favourite_addresses = ppss.predictoor_ss.my_addresses

return app
29 changes: 29 additions & 0 deletions pdr_backend/analytics/predictoor_dashboard/test/resources.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import time

from enforce_typing import enforce_types
from selenium.webdriver.common.keys import Keys

from pdr_backend.lake.duckdb_data_store import DuckDBDataStore
from pdr_backend.ppss.ppss import mock_ppss
Expand Down Expand Up @@ -33,3 +36,29 @@ def _clear_test_db(directory: str):
db.drop_table(Payout.get_lake_table_name())
db.drop_table(Prediction.get_lake_table_name())
db.duckdb_conn.close()


def _input_action(dash_duo, input_id, table_id, input_value, expected_rows):
"""
Helper function to test the search input in the tables
It sends the input_value to the search input and checks
if the number of rows in the table is as expected
"""

search_input = dash_duo.find_element(input_id)
search_input.clear()
search_input.send_keys(input_value + Keys.ENTER)
time.sleep(2)
assert len(dash_duo.find_elements(f"{table_id} tbody tr")) == expected_rows


def start_server_and_wait(dash_duo, app):
"""
Start the server and wait for the elements to be rendered.
"""

dash_duo.start_server(app)
dash_duo.wait_for_element("#feeds_table")
dash_duo.wait_for_element("#predictoors_table")
dash_duo.wait_for_element("#feeds_table tbody tr")
dash_duo.wait_for_element("#predictoors_table tbody tr")
184 changes: 92 additions & 92 deletions pdr_backend/analytics/predictoor_dashboard/test/test_callbacks.py
Original file line number Diff line number Diff line change
@@ -1,116 +1,116 @@
import time
import pytest

from dash import Dash
import dash_bootstrap_components as dbc
from selenium.webdriver.common.keys import Keys

from pdr_backend.analytics.predictoor_dashboard.dash_components.callbacks import (
get_callbacks,
)
from pdr_backend.analytics.predictoor_dashboard.dash_components.view_elements import (
get_layout,
)
from pdr_backend.analytics.predictoor_dashboard.dash_components.util import (
get_feeds_data_from_db,
get_predictoors_data_from_payouts,
get_user_payouts_stats_from_db,
)
from pdr_backend.analytics.predictoor_dashboard.test.resources import (
_prepare_test_db,
_clear_test_db,
_input_action,
start_server_and_wait,
)
from pdr_backend.lake.payout import Payout
from pdr_backend.lake.prediction import Prediction


@pytest.fixture
def _test_analytics_app():
app = Dash(__name__)
get_callbacks(app)
return app
def test_get_input_data_from_db(setup_app, dash_duo):
app = setup_app
start_server_and_wait(dash_duo, app)


def test_startup(
tmpdir,
_sample_daily_predictions,
_sample_payouts_related_with_predictions,
dash_duo,
):
_clear_test_db(str(tmpdir))
def test_feeds_search_input(setup_app, dash_duo):
"""
Test the search input in the "Feeds" table.
The search input is used to filter the feeds by their name.
"""

_prepare_test_db(
tmpdir,
_sample_payouts_related_with_predictions,
table_name=Payout.get_lake_table_name(),
)
app = setup_app
start_server_and_wait(dash_duo, app)
_input_action(dash_duo, "#search-input-Feeds", "#feeds_table", "OCEAN", 1)
_input_action(dash_duo, "#search-input-Feeds", "#feeds_table", "BTC", 2)
_input_action(dash_duo, "#search-input-Feeds", "#feeds_table", "ADA", 2)

ppss, _ = _prepare_test_db(
tmpdir,
_sample_daily_predictions,
table_name=Prediction.get_lake_table_name(),
)

app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.config["suppress_callback_exceptions"] = True
app.layout = get_layout()
app.lake_dir = ppss.lake_ss.lake_dir
app.feeds_data = get_feeds_data_from_db(ppss.lake_ss.lake_dir)
app.predictoors_data = get_predictoors_data_from_payouts(
get_user_payouts_stats_from_db(ppss.lake_ss.lake_dir)
def test_predictoors_search_input(setup_app, dash_duo):
"""
Test the search input in the "Predictoors" table.
The search input is used to filter the predictoors by their name.
"""

app = setup_app
start_server_and_wait(dash_duo, app)

_input_action(
dash_duo, "#search-input-Predictoors", "#predictoors_table", "0xaaa", 1
)
_input_action(
dash_duo, "#search-input-Predictoors", "#predictoors_table", "0xd2", 2
)
app.favourite_addresses = ppss.predictoor_ss.my_addresses
get_callbacks(app)

dash_duo.start_server(app)
dash_duo.wait_for_element("#feeds_table")
dash_duo.wait_for_element("#predictoors_table")

dash_duo.wait_for_element("#feeds_table tbody tr")
dash_duo.wait_for_element("#predictoors_table tbody tr")
def test_checkbox_selection(setup_app, dash_duo):
"""
Test the selection of checkboxes in the "Feeds" and "Predictoors" tables.
"""

# ----TEST SEARCH INPUT WITH "OCEAN" VALUE----#
dash_duo.find_element("#search-input-Feeds").send_keys("OCEAN" + Keys.ENTER)
# wait 5 seconds
time.sleep(2)
assert len(dash_duo.find_elements("#feeds_table tbody tr")) == 1
app = setup_app
start_server_and_wait(dash_duo, app)

# click on the checkbox in the second row of the "Feeds" table
dash_duo.find_element("#feeds_table tbody tr:nth-child(2) input").click()

# click on the checkbox in the first row of the "Predictoors" table
dash_duo.find_element("#predictoors_table tbody tr input").click()

# ----TEST SEARCH INPUT WITH "BTC" VALUE----#
# reset the search input
dash_duo.find_element("#search-input-Feeds").clear()
# enter "BTC" phrase to the "search-input-Feeds" input
dash_duo.find_element("#search-input-Feeds").send_keys("BTC" + Keys.ENTER)

def test_timeframe_metrics(setup_app, dash_duo):
"""
Test the metrics that are displayed when a predictoor is selected.
It takes the predictoor row from the table and compares with the top metrics.

The metrics are: Profit, Accuract, Stake
"""

app = setup_app
start_server_and_wait(dash_duo, app)

dash_duo.find_element("#predictoors_table tbody tr:nth-child(3) input").click()
time.sleep(2)
# check if the "Feeds" table has only two rows
assert len(dash_duo.find_elements("#feeds_table tbody tr")) == 2

# ----TEST SEARCH INPUT WITH "ADA" VALUE----#
# reset the search input
dash_duo.find_element("#search-input-Feeds").clear()
# enter "BTC" phrase to the "search-input-Feeds" input
dash_duo.find_element("#search-input-Feeds").send_keys("ADA" + Keys.ENTER)

dash_duo.find_element("#feeds_table tbody tr:nth-child(2) input").click()
time.sleep(2)

# check if the "Feeds" table has only two rows
assert len(dash_duo.find_elements("#feeds_table tbody tr")) == 2
table_profit = dash_duo.find_element(
"#predictoors_table tbody tr:nth-child(3) td:nth-child(3)"
).text
metric_profit = dash_duo.find_element("#profit_metric").text
assert table_profit + " OCEAN" == metric_profit

# ----TEST PREDICTOOR INPUT WITH "0xaaa" VALUE----#
dash_duo.find_element("#search-input-Predictoors").send_keys("0xaaa" + Keys.ENTER)
time.sleep(2)
assert len(dash_duo.find_elements("#predictoors_table tbody tr")) == 1
table_accuracy = dash_duo.find_element(
"#predictoors_table tbody tr:nth-child(3) td:nth-child(4)"
).text
metric_accuracy = dash_duo.find_element("#accuracy_metric").text
assert table_accuracy + ".0%" == metric_accuracy

table_stake = dash_duo.find_element(
"#predictoors_table tbody tr:nth-child(3) td:nth-child(5)"
).text
metric_stake = dash_duo.find_element("#stake_metric").text
assert table_stake + " OCEAN" == metric_stake

# ----TEST PREDICTOOR INPUT WITH "0xd2" VALUE----#
# clear input
dash_duo.find_element("#search-input-Predictoors").clear()
# enter "0xd2" phrase to the "search-input-Predictoors" input
dash_duo.find_element("#search-input-Predictoors").send_keys("0xd2" + Keys.ENTER)

def test_predictoors_feed_only_switch(setup_app, dash_duo):
"""
Test the switch that toggles between showing only the feeds that are
associated with the selected predictoor and all feeds.
"""

app = setup_app
start_server_and_wait(dash_duo, app)

dash_duo.find_element("#predictoors_table tbody tr:nth-child(3) input").click()
time.sleep(2)

# check if the "Predictoors" table has only one row
assert len(dash_duo.find_elements("#predictoors_table tbody tr")) == 2
feeds_table_len = len(dash_duo.find_elements("#feeds_table tbody tr"))
assert feeds_table_len == 2

# click on the checkbox in the second row of the "Feeds" table
dash_duo.find_element("#feeds_table tbody tr:nth-child(2) input").click()
# Scroll the toggle switch into view and click using JavaScript
toggle_switch = dash_duo.find_element("#toggle-switch-predictoor-feeds")
dash_duo.driver.execute_script("arguments[0].scrollIntoView(true);", toggle_switch)
dash_duo.driver.execute_script("arguments[0].click();", toggle_switch)
time.sleep(2)

# click on the checkbox in the first row of the "Predictoors" table
dash_duo.find_element("#predictoors_table tbody tr input").click()
feeds_table_len = len(dash_duo.find_elements("#feeds_table tbody tr"))
assert feeds_table_len == 6
Loading