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

[WIP] Unit Test for dashboard #672

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
4456665
Added IDs to UI components
proy30 Aug 10, 2024
f4219ca
Added some more IDs to UI components
proy30 Aug 11, 2024
7f3bbbb
Added 'close' button in lattice settings
proy30 Aug 11, 2024
b067042
Removed functionality that converted user input
proy30 Aug 11, 2024
fd50751
Added unit test for running FODO simulation on dashboard
proy30 Aug 11, 2024
9fd2b54
Added subprocess to start up server with pytest
proy30 Aug 11, 2024
583f227
Attempt to fix flaky test
proy30 Aug 12, 2024
f7e0e83
Add seleniumbase dependency in CI workflows for testing
proy30 Aug 12, 2024
e5f9e94
Added dashboard dependencies in stubs.yml
proy30 Aug 12, 2024
91fa693
Added missing dashboard dependencies to stubs.yml
proy30 Aug 12, 2024
703d73c
Update CI & Requirements Files
ax3l Aug 12, 2024
c94b280
pytest: Graceful Skipping
ax3l Aug 12, 2024
962efe7
Merge remote-tracking branch 'mainline/development' into selenium
ax3l Aug 12, 2024
2e5588c
Unit test interacts with phase space projections
proy30 Aug 18, 2024
e3fec39
time adjustments
proy30 Aug 18, 2024
7bbf231
adjust format
proy30 Aug 18, 2024
e493deb
attempt to run headless
proy30 Aug 18, 2024
36c7928
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 18, 2024
42c5972
Fixed UnboundLocalError and PermissionError
proy30 Sep 19, 2024
52c1dda
Replace time.sleep() with alternative
proy30 Sep 19, 2024
8a1b787
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 19, 2024
fcd0138
Redid logic to show simulation is complete
proy30 Sep 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
python3 -m venv py-venv
source py-venv/bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade build packaging setuptools wheel pytest
python3 -m pip install --upgrade build packaging setuptools wheel
python3 -m pip install --upgrade -r requirements_mpi.txt
python3 -m pip install --upgrade -r src/python/impactx/dashboard/requirements.txt
python3 -m pip install --upgrade -r examples/requirements.txt
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ def on_distribution_type_change(**kwargs):

@ctrl.add("updateDistributionParameters")
def on_distribution_parameter_change(parameter_name, parameter_value, parameter_type):
parameter_value, input_type = generalFunctions.determine_input_type(parameter_value)
error_message = generalFunctions.validate_against(parameter_value, parameter_type)

update_distribution_parameters(parameter_name, parameter_value, error_message)
Expand Down Expand Up @@ -175,13 +174,15 @@ def card():
with vuetify.VCol(cols=8):
vuetify.VCombobox(
label="Select Distribution",
id="selected_distribution",
v_model=("selectedDistribution",),
items=("listOfDistributions",),
dense=True,
)
with vuetify.VCol(cols=4):
vuetify.VSelect(
v_model=("selectedDistributionType",),
id="selected_distribution_type",
label="Type",
items=(["Native", "Twiss"],),
# change=(ctrl.kin_energy_unit_change, "[$event]"),
Expand All @@ -199,6 +200,7 @@ def card():
):
vuetify.VTextField(
label=("parameter.parameter_name",),
id=("parameter.parameter_name",),
v_model=("parameter.parameter_default_value",),
change=(
ctrl.updateDistributionParameters,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ def card(self):
vuetify.VCombobox(
v_model=("particle_shape",),
label="Particle Shape",
id="particle_shape",
items=([1, 2, 3],),
dense=True,
)
Expand All @@ -101,6 +102,7 @@ def card(self):
vuetify.VTextField(
v_model=("npart",),
label="Number of Particles",
id="npart",
error_messages=("npart_validation",),
change=(
ctrl.on_input_change,
Expand All @@ -114,6 +116,7 @@ def card(self):
vuetify.VTextField(
v_model=("kin_energy",),
label="Kinetic Energy",
id="kin_energy",
error_messages=("kin_energy_validation",),
change=(
ctrl.on_input_change,
Expand All @@ -127,6 +130,7 @@ def card(self):
vuetify.VSelect(
v_model=("kin_energy_unit",),
label="Unit",
id="kin_energy_unit",
items=(["meV", "eV", "keV", "MeV", "GeV", "TeV"],),
change=(ctrl.kin_energy_unit_change, "[$event]"),
dense=True,
Expand All @@ -135,6 +139,7 @@ def card(self):
with vuetify.VCol(cols=8, classes="py-0"):
vuetify.VTextField(
label="Bunch Charge",
id="bunch_charge_C",
v_model=("bunch_charge_C",),
error_messages=("bunch_charge_C_validation",),
change=(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ def on_add_lattice_element_click():
def on_lattice_element_parameter_change(
index, parameter_name, parameter_value, parameter_type
):
parameter_value, input_type = generalFunctions.determine_input_type(parameter_value)
error_message = generalFunctions.validate_against(parameter_value, parameter_type)

update_latticeElement_parameters(
Expand Down Expand Up @@ -253,6 +252,7 @@ def card():
with vuetify.VCol(cols=8):
vuetify.VCombobox(
label="Select Accelerator Lattice",
id="selected_lattice",
v_model=("selectedLattice", None),
items=("listOfLatticeElements",),
error_messages=("isSelectedLatticeListEmpty",),
Expand All @@ -262,6 +262,7 @@ def card():
with vuetify.VCol(cols="auto"):
vuetify.VBtn(
"ADD",
id="add_button",
color="primary",
dense=True,
classes="mr-2",
Expand All @@ -270,6 +271,7 @@ def card():
with vuetify.VCol(cols="auto"):
vuetify.VBtn(
"CLEAR",
id="clear_button",
color="secondary",
dense=True,
classes="mr-2",
Expand All @@ -279,6 +281,7 @@ def card():
vuetify.VIcon(
"mdi-cog",
click="showDialog_settings = true",
id="lattice_settings_icon",
)
with vuetify.VRow():
with vuetify.VCol():
Expand Down Expand Up @@ -337,6 +340,7 @@ def card():
):
vuetify.VTextField(
label=("parameter.parameter_name",),
id=("parameter.parameter_name + index",),
v_model=(
"parameter.parameter_default_value",
),
Expand Down Expand Up @@ -420,6 +424,7 @@ def dialog_lattice_settings():
with vuetify.VCol(no_gutters=True):
vuetify.VTextField(
v_model=("nsliceDefaultValue",),
id="nslice_default_value",
change=(
ctrl.nsliceDefaultChange,
"['nslice', $event]",
Expand All @@ -431,3 +436,13 @@ def dialog_lattice_settings():
style="max-width: 75px",
classes="ma-0 pa-0",
)
vuetify.VDivider()
with vuetify.VCardActions():
vuetify.VSpacer()
vuetify.VBtn(
"Close",
id="lattice_settings_close",
color="primary",
text=True,
click="showDialog_settings = false",
)
2 changes: 1 addition & 1 deletion src/python/impactx/dashboard/Input/trameFunctions.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ def create_route(route_title, mdi_icon):
with vuetify.VListItemIcon():
vuetify.VIcon(mdi_icon)
with vuetify.VListItemContent():
vuetify.VListItemTitle(route_title)
vuetify.VListItemTitle(route_title, id=f"{route_title}_route")
2 changes: 2 additions & 0 deletions src/python/impactx/dashboard/Toolbar/toolbarMain.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def plot_options():
v_model=("active_plot", "1D plots over s"),
items=("plot_options",),
label="Select plot to view",
id="select_plot",
hide_details=True,
dense=True,
style="max-width: 250px",
Expand All @@ -39,6 +40,7 @@ def plot_options():
def run_simulation_button():
vuetify.VBtn(
"Run Simulation",
id="run_simulation_button",
style="background-color: #00313C; color: white; margin: 0 20px;",
click=ctrl.run_simulation,
disabled=("disableRunSimulationButton", True),
Expand Down
2 changes: 1 addition & 1 deletion src/python/impactx/dashboard/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
# GUI
# -----------------------------------------------------------------------------
def init_terminal():
with xterm.XTerm(v_if="$route.path == '/Run'") as term:
with xterm.XTerm(v_show="$route.path == '/Run'", id="xterm_component") as term:
ctrl.terminal_print = term.writeln


Expand Down
82 changes: 82 additions & 0 deletions tests/python/dashboard/test_dashboard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import importlib

from util import set_input_value, start_dashboard, wait_for_ready
import time

import pytest


@pytest.mark.skipif(
importlib.util.find_spec("seleniumbase") is None, reason="seleniumbase is not available"
)
def test_simulation():
proy30 marked this conversation as resolved.
Show resolved Hide resolved
"""
This test runs the FODO example on the dashboard and verifies
that the simulation has ran successfully.
"""
from seleniumbase import SB

app_process = start_dashboard()
proy30 marked this conversation as resolved.
Show resolved Hide resolved
time.sleep(10)
proy30 marked this conversation as resolved.
Show resolved Hide resolved

try:
with SB() as sb:
Copy link
Member

Choose a reason for hiding this comment

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

Trying if this prevents that the unit test on Linux/macOS opens a browser:

Suggested change
with SB() as sb:
with SB(headless=True) as sb:

Copy link
Member

Choose a reason for hiding this comment

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

url = "http://localhost:8080/index.html#/Input"
sb.open(url)

wait_for_ready(sb, 60)

# Adjust beam properties
sb.click("#particle_shape")
sb.click("div.v-list-item:nth-of-type(2)")
set_input_value(sb, "npart", 10000)
set_input_value(sb, "kin_energy", 2.0e3)
set_input_value(sb, "bunch_charge_C", 1.0e-9)

# Adjust beam distribution
set_input_value(sb, "selected_distribution", "Waterbag")
set_input_value(sb, "lambdaX", 3.9984884770e-5)
set_input_value(sb, "lambdaY", 3.9984884770e-5)
set_input_value(sb, "lambdaT", 1.0e-3)
set_input_value(sb, "lambdaPx", 2.6623538760e-5)
set_input_value(sb, "lambdaPy", 2.6623538760e-5)
set_input_value(sb, "lambdaPt", 2.0e-3)
set_input_value(sb, "muxpx", -0.846574929020762)
set_input_value(sb, "muypy", 0.846574929020762)
set_input_value(sb, "mutpt", 0.0)

# Adjust lattice configuration
sb.click("#lattice_settings_icon")
sb.sleep(1)
set_input_value(sb, "nslice_default_value", 25)
sb.click("#lattice_settings_close")
sb.click("#clear_button")
set_input_value(sb, "selected_lattice", "Drift")
sb.click("#add_button")
set_input_value(sb, "ds0", 0.25)
set_input_value(sb, "selected_lattice", "Quad")
sb.click("#add_button")
set_input_value(sb, "ds1", 1.0)
set_input_value(sb, "k1", 1.0)
set_input_value(sb, "selected_lattice", "Drift")
sb.click("#add_button")
set_input_value(sb, "ds2", 0.5)
set_input_value(sb, "selected_lattice", "Quad")
sb.click("#add_button")
set_input_value(sb, "ds3", 1.0)
set_input_value(sb, "k3", -1.0)
set_input_value(sb, "selected_lattice", "Drift")
sb.click("#add_button")
set_input_value(sb, "ds4", 0.25)

# Run simulation
sb.click("#Run_route")
sb.sleep(1)
sb.click("#run_simulation_button")
sb.sleep(25)

# Check if "Simulation complete" message is printed
xterm_content = sb.get_text("#xterm_component")
assert "Simulation complete." in xterm_content
finally:
app_process.terminate()
40 changes: 40 additions & 0 deletions tests/python/dashboard/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import os
import subprocess


def wait_for_ready(sb, timeout=60):
for i in range(timeout):
print(f"wait_for_ready {i}")
if sb.is_element_present(".trame__loader"):
sb.sleep(1)
else:
print("Ready")
proy30 marked this conversation as resolved.
Show resolved Hide resolved
return


def set_input_value(sb, element_id, value):
"""
Function to clear, update, and trigger a change event on an input field by ID.
"""
selector = f"#{element_id}"
sb.clear(selector)

if not isinstance(value, str):
value = str(value)

sb.update_text(selector, value)
sb.send_keys(selector, "\n")


def start_dashboard():
"""
Function which starts up impactx-dashboard server.
"""
script_dir = os.path.dirname(os.path.abspath(__file__))
working_directory = os.path.join(script_dir, "../../../src/python/impactx")
working_directory = os.path.normpath(working_directory)
Comment on lines +78 to +80
Copy link
Member

Choose a reason for hiding this comment

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

We need to generalize this so it:

  • runs in CTests from the build directory
  • runs when copied in a package manager with external installs


return subprocess.Popen(
["python", "-m", "dashboard"],
Copy link
Member

Choose a reason for hiding this comment

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

When I run this locally, this literally opens a browser window for me.
Is there a way to run this headless?

cwd=working_directory
)
1 change: 1 addition & 0 deletions tests/python/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
-r ../../examples/requirements.txt
pytest
seleniumbase
Loading