diff --git a/.github/workflows/test_and_build.yaml b/.github/workflows/test_and_build.yaml index 65e2ea92..635c1f02 100644 --- a/.github/workflows/test_and_build.yaml +++ b/.github/workflows/test_and_build.yaml @@ -83,7 +83,7 @@ jobs: shell: bash -l {0} run: | export RUBIN_SIM_DATA_DIR=~/rubin_sim_data - pytest -r a -v --cov=schedview --cov=tests --cov-report=xml --cov-report=term --cov-branch + python -m pytest -r a -v --cov=schedview --cov=tests --cov-report=xml --cov-report=term --cov-branch - name: Upload coverage to codecov uses: codecov/codecov-action@v4 diff --git a/.gitignore b/.gitignore index 42135f3f..1f67b453 100644 --- a/.gitignore +++ b/.gitignore @@ -150,3 +150,4 @@ notebooks/MPCORB.DAT.gz # Other temporary files tmp/* util/tmp/* +.vscode/settings.json diff --git a/docs/playwright_testing.rst b/docs/playwright_testing.rst new file mode 100644 index 00000000..49f56f13 --- /dev/null +++ b/docs/playwright_testing.rst @@ -0,0 +1,76 @@ +Playwright Testing +================== + +Install playwright + browsers in environment +-------------------------------------------- +`Install info `_ + +:: + + conda activate schedview + pip install playwright + playwright install # install browsers + +Depending on your OS, you may also need to run: + +:: + + playwright install-deps + +Run (headless) tests +-------------------- +Tests should be run from the root (where the tests are run during workflow). +The playwright tests are disabled by default, so, to run them, the environment variable ENABLE_PLAYWRIGHT_TESTS must be set. + +:: + + ENABLE_PLAYWRIGHT_TESTS=1 pytest tests/test_scheduler_dashboard.py + +Headed tests +------------ +Tests will run in headless mode by default. If you would like to see the tests in action (headed), the tests must be run locally (where the LFA mode test will fail) or using NoMachine (or a supported equivalent). + +`Instruction to set up NoMachine `_ + +In the code, also comment/uncommment the following lines in each of the 3 classes' SetUpClass functions to swap to headed mode: + +:: + + cls.browser = cls.playwright.chromium.launch(headless=True) # comment this out and uncomment the other lines + # cls.browser = cls.playwright.chromium.launch( + # headless=False, + # slow_mo=100 + # ) + +Increase the 'slow_mo' parameter to slow the tests down (e.g. 500). + +Debug tests +----------- +Visualise stepping through actions and assertions. Again, this needs to be run locally or using NoMachine. + +`Docs `_ + +:: + + ENABLE_PLAYWRIGHT_TESTS=1 PWDEBUG=1 pytest -s tests/test_scheduler_dashboard.py + +Note that when actions are taken that initiate the loading indicator + pop-up messages (e.g. load pickle, change date, reset dashboard), the assertions need to be stepped through very quickly or else the events will be missed and the test will fail. Be prepared for button mashing. + +Test Generator +-------------- +Generates testing code as you perform actions. Again, this needs to be run locally or using NoMachine. + +`Docs `_ + +Launch server from one terminal: + +:: + + python schedview/app/scheduler_dashboard/scheduler_dashboard.py + +Run test generator from another terminal: + +:: + + playwright codegen http://localhost:8080/schedview-snapshot/dashboard + diff --git a/pyproject.toml b/pyproject.toml index e7562502..25ed0503 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,9 @@ test = [ "isort", "ruff", "pytest-cov", - "geckodriver" + "geckodriver", + "playwright", + "pre-commit" ] dev = [ "documenteer[guide]", diff --git a/test-requirements.txt b/test-requirements.txt index b098d88e..4b4429e7 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -7,3 +7,4 @@ geckodriver pre-commit boto3 botocore +playwright diff --git a/tests/test_scheduler_dashboard.py b/tests/test_scheduler_dashboard.py index 59386183..5c0fa2f5 100644 --- a/tests/test_scheduler_dashboard.py +++ b/tests/test_scheduler_dashboard.py @@ -1,4 +1,9 @@ +import functools import importlib.resources +import os +import re +import subprocess +import time import unittest from collections import OrderedDict from pathlib import Path @@ -12,11 +17,16 @@ from astropy.time import Time from pandas import Timestamp from panel.widgets import Tabulator + +# Conditional install for CI workflow. +if os.environ.get("ENABLE_PLAYWRIGHT_TESTS", "false").lower() not in ("", "0", "f", "false"): + from playwright.sync_api import expect, sync_playwright + from rubin_scheduler.scheduler.example import example_scheduler from rubin_scheduler.scheduler.features.conditions import Conditions from rubin_scheduler.scheduler.model_observatory import ModelObservatory -# Objects to test instances against +# Objects to test instances against. from rubin_scheduler.scheduler.schedulers.core_scheduler import CoreScheduler import schedview @@ -26,41 +36,38 @@ scheduler_app, ) -# Schedview methods +# Schedview methods. from schedview.compute.scheduler import make_scheduler_summary_df from schedview.compute.survey import compute_maps -""" -Tests I usually perform: - - 1. Load valid pickle. - 2. Choose date. - 3. Choose tier. - 4. Select survey. - 5. Select basis function (array). - 6. Select basis function (finite scalar). - 7. Select basis function (-inf scalar). - 8. Choose survey map (basis function). - 9. Choose survey map (sky brightness). - 10. Choose nside. - 11. Choose color palette. - 12. Check tooltip reflects selection data. - 13. Choose invalid date. - 14. Choose invalid pickle. - 15. Load two pickles, one after the other. - - -Notes - - - Unit tests are not supposed to rely on each other. - - Unit tests are run in alphabetical order. - -""" - TEST_PICKLE = str(importlib.resources.files(schedview).joinpath("data", "sample_scheduler.pickle.xz")) MJD_START = get_sky_brightness_date_bounds()[0] TEST_DATE = Time(MJD_START + 0.2, format="mjd").datetime DEFAULT_TIMEZONE = "America/Santiago" +HEADLESS = True +ISO8601_REGEX = re.compile(r"20[23][0-9]-[01][0-9]-[0-3][0-9][ T][0-2][0-9]:[0-5][0-9]:[0-5][0-9]") + +try: + PORT = int(os.environ["SCHEDULER_SNAPSHOT_DASHBOARD_PORT"]) +except KeyError: + PORT = 8888 + +# Set timeout for Playwright tests to 10 seconds. +if os.environ.get("ENABLE_PLAYWRIGHT_TESTS", "false").lower() not in ("", "0", "f", "false"): + expect.set_options(timeout=10_000) + + +# Custom decorator to skip all tests in a class. +# Applied to playwright tests to reduce CI workflow time. +# Tests can be manually enabled by setting env variable. +def skip_playwright_tests(cls): + if os.environ.get("ENABLE_PLAYWRIGHT_TESTS", "false").lower() in ("", "0", "f", "false"): + for name, method in cls.__dict__.items(): + if name.startswith("test_") or name in ("setUpClass", "tearDownClass"): + setattr( + cls, name, functools.wraps(method)(unittest.skip("Playwright tests disabled")(method)) + ) + return cls class TestSchedulerDashboard(unittest.TestCase): @@ -151,5 +158,473 @@ def test_site_models(self): self.assertEqual(seeing_data.fwhm_500, fiducial_seeing) +@skip_playwright_tests +class TestStandardModeE2E(unittest.TestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.playwright = sync_playwright().start() + if HEADLESS: + cls.browser = cls.playwright.chromium.launch(headless=True) + else: + cls.browser = cls.playwright.chromium.launch(headless=False, slow_mo=100) + + cls.dashboard_process = subprocess.Popen( + ["python", "schedview/app/scheduler_dashboard/scheduler_dashboard.py"] + ) + # Allow the dashboard time to start. + time.sleep(10) + + @classmethod + def tearDownClass(cls): + super().tearDownClass() + cls.browser.close() + cls.playwright.stop() + + if hasattr(cls, "dashboard_process") and cls.dashboard_process: + cls.dashboard_process.terminate() + cls.dashboard_process.wait() + + def test_without_data(self): + page = self.browser.new_page() + page.goto(f"http://localhost:{PORT}/schedview-snapshot") + + # Check page title. + expect(page).to_have_title(re.compile("Scheduler Dashboard")) + + # Check logo. + img_element = page.get_by_role("img").first + img_element.wait_for() + img_src = img_element.get_attribute("src") + page_img = self.browser.new_page() + page_img.goto(f"http://localhost:{PORT}{img_src}") + expect(page_img).not_to_have_title("404: Not Found") + page_img.close() + + # Check dashboard heading. + expect(page.locator("pre").nth(0)).to_contain_text("Scheduler Dashboard") + + # Check dashboard sub-heading is blank. + expect(page.locator("pre").nth(1)).to_be_empty() + + # Check 3x section headings. + expect(page.locator("pre").nth(2)).to_contain_text("Scheduler summary") + expect(page.locator("pre").nth(3)).to_contain_text("Basis functions & rewards") + expect(page.locator("pre").nth(4)).to_contain_text("Map") + + # Check 'no data' messages. + expect(page.get_by_role("paragraph").nth(0)).to_contain_text("No summary available.") + expect(page.get_by_role("paragraph").nth(1)).to_contain_text("No rewards available.") + expect(page.get_by_role("paragraph").nth(2)).to_contain_text("No scheduler loaded.") + + # Check tier drop down is empty. + expect(page.get_by_role("combobox").nth(1)).to_be_empty() + # Check date is as expected. + initial_date = MJD_START.to_datetime().strftime("%Y-%m-%d %H:%M:%S") + expect(page.get_by_role("textbox")).to_have_value(initial_date) + # Check map drop-down has (only) reward. + expect(page.get_by_role("combobox").nth(2)).to_have_value("reward") + + # Change date. + page.get_by_role("textbox").click() + if TEST_DATE.month == 12: + change_date = TEST_DATE.replace(year=TEST_DATE.year + 1, month=1) + else: + change_date = TEST_DATE.replace(month=TEST_DATE.month + 1) + date_string = f"{change_date.strftime('%B')} 1," + page.get_by_label(date_string).click() + page.get_by_label("Hour").press("Enter") + # Restore loading conditions. + page.get_by_role("button", name="﫽 Restore Loading Conditions").click() + # Change nside. + page.get_by_role("combobox").nth(3).select_option("4") + # Change color palette. + page.get_by_role("combobox").nth(4).select_option("Turbo256") + + # Check selections don't cause problems. + expect(page.locator("pre").nth(6)).not_to_contain_text("Traceback") + # Close debugging panel + page.get_by_role("button", name="Debugging").click() + + def test_with_data(self): + page = self.browser.new_page() + page.goto(f"http://localhost:{PORT}/schedview-snapshot") + + # Load data from pickle. + page.get_by_role("combobox").nth(0).select_option(TEST_PICKLE[1:]) # Remove leading '/' + + # Check loading indicator. + indicator_div = page.locator("div").filter(has_text="Scheduler Dashboard").nth(1) + expect(indicator_div).to_have_attribute("class", "bk-GridBox pn-loading pn-arc") + + # Check 4x info messages displayed. + expect(page.get_by_text("Scheduler loading...").first).to_be_visible() + expect(page.get_by_text("Scheduler pickle loaded successfully!").first).to_be_visible() + expect(page.get_by_text("Making scheduler summary dataframe...").first).to_be_visible() + expect(page.get_by_text("Scheduler summary dataframe updated successfully").first).to_be_visible() + + # Check subheading. + expect(page.locator("pre").nth(1)).to_contain_text("Tier 0 - Survey 0 - Map reward") + # Check date. + expect(page.get_by_role("textbox")).to_have_value(ISO8601_REGEX) + # Check survey docs link works. + with page.expect_popup() as page_survey_docs_info: + row = page.get_by_role("row").nth(1) + link = row.get_by_role("link") + link.click() + page_survey_docs = page_survey_docs_info.value + # TODO: Check something about docs page. + page_survey_docs.close() + # Check bf docs link works. (vulnerable to data changes) + with page.expect_popup() as page_bf_docs_info: + row = page.get_by_role("row", name="AvoidDirectWind") + link = row.get_by_role("link") + link.click() + page_bf_docs = page_bf_docs_info.value + # TODO: Check something about docs page. + page_bf_docs.close() + + # Select tier 5. (vulnerable to data changes) + page.get_by_role("combobox").nth(1).select_option("tier 5") + # Wait for update to complete. + page.wait_for_selector("text=Tier 5 - Survey 0 - Map reward") + # Select survey 1. + page.get_by_role("row").nth(2).click() + # Wait for update to complete. + page.wait_for_selector("text=Tier 5 - Survey 1 - Map reward") + + # Check 3x headings updated. + survey_name = page.get_by_role("row").nth(2).get_by_role("gridcell").nth(1).text_content() + expect(page.locator("pre").nth(2)).to_contain_text("Scheduler summary for tier 5") + expect(page.locator("pre").nth(3)).to_contain_text( + f"Basis functions & rewards for survey {survey_name}" + ) + expect(page.locator("pre").nth(4)).to_contain_text(f"Survey {survey_name} Map: reward") + # Check 2x tables visible. + expect(page.get_by_role("grid")).to_have_count(2) + # Check 1x map/key visible. + expect(page.locator(".bk-Canvas > div:nth-child(11)")).to_be_visible() + + # Select u_sky from Survey map. + page.get_by_role("combobox").nth(2).select_option("u_sky") + # Check map heading changed. + expect(page.locator("pre").nth(4)).to_contain_text(f"Survey {survey_name} Map: u_sky") + + # Select M5Diff from Survey map. (vulnerable to data changes) + map_option = page.locator("option", has_text="M5Diff i").text_content() + page.get_by_role("combobox").nth(2).select_option(map_option) + # Check M5Diff row highlighted in bf table. + expect(page.get_by_role("row", name="M5Diff i")).to_have_class(re.compile(r"tabulator-selected")) + + # Select MoonAvoidance row in bf table. (vulnerable to data changes) + page.get_by_text("MoonAvoidance", exact=True).click() + # Check map heading changed. + expect(page.locator("pre").nth(4)).to_contain_text(f"Survey {survey_name} Reward: MoonAvoidance") + # Check Survey map drop-down value = MoonAvoidance. + map_option = page.locator("option", has_text="MoonAvoidance").text_content() + expect(page.get_by_role("combobox").nth(2)).to_have_value(map_option) + + # Select FilterChange row in bf table. (vulnerable to data changes) + page.get_by_text("FilterChange i").click() + # Check Survey map drop-down shows “”. + expect(page.get_by_role("combobox").nth(2)).to_have_value("") + + # Select Tier 3. (vulnerable to data changes) + page.get_by_role("combobox").nth(1).select_option("tier 3") + + # Check Survey row 0 shows Reward='TimeToTwilight, NightModulo'. + # (vulnerable to data changes) + survey_0_reward = page.get_by_role("row").nth(1).get_by_role("gridcell").nth(2) + expect(survey_0_reward).to_have_text("TimeToTwilight, NightModulo") + + # Check TimeToTwilight infeasiblility displayed correctly. + # (vulnerable to data changes) + img = page.get_by_role("row").filter(has_text="TimeToTwilight").get_by_role("img") + fill_value = img.evaluate('(element) => element.querySelector("path").getAttribute("fill")') + assert fill_value == "#CE1515" + expect( + page.get_by_role("rowgroup").nth(5).get_by_role("row").nth(15).get_by_role("gridcell").nth(3) + ).to_contain_text("-Infinity") + + # Change date. + page.get_by_role("textbox").click() # open date picker + page.get_by_label("May 3,").click() # change day + page.get_by_label("Hour").fill("03") # change hour + page.locator("div:nth-child(3) > .arrowUp").click() # change minute + page.locator("div:nth-child(5) > .arrowUp").click() # change second + page.get_by_text("::").press("Enter") # submit + + # Check loading indicator. + indicator_div = page.locator("div").filter(has_text="Scheduler Dashboard").nth(1) + expect(indicator_div).to_have_attribute("class", "bk-GridBox pn-loading pn-arc") + + # Check 4x info messages displayed. + expect(page.get_by_text("Updating Conditions object...").first).to_be_visible() + expect(page.get_by_text("Conditions object updated successfully").first).to_be_visible() + expect(page.get_by_text("Making scheduler summary dataframe...").first).to_be_visible() + expect(page.get_by_text("Scheduler summary dataframe updated successfully").first).to_be_visible() + + # Select tier 3. (vulnerable to data changes) + page.get_by_role("combobox").nth(1).select_option("tier 3") + + # Check survey row 0 shows Reward = 11.something + # (vulnerable to data changes) + survey_0_reward = page.get_by_role("row").nth(1).get_by_role("gridcell").nth(2) + expect(survey_0_reward).to_have_text(re.compile(r"11\.7\d")) + + # Check TimeToTwilight feasiblility displayed correctly. + # (vulnerable to data changes) + img = page.get_by_role("row").filter(has_text="TimeToTwilight").get_by_role("img") + fill_value = img.evaluate('(element) => element.querySelector("path").getAttribute("fill")') + assert fill_value == "#2DC214" + expect( + page.get_by_role("rowgroup").nth(5).get_by_role("row").nth(15).get_by_role("gridcell").nth(3) + ).to_contain_text("0.000") + + # Make some changes to check restore works as expected. + # Select Survey map u_sky. + page.get_by_role("combobox").nth(2).select_option("u_sky") + # Select map resolution = 4. + page.get_by_role("combobox").nth(3).select_option("4") + # Select color palette = Inferno256. + page.get_by_role("combobox").nth(4).select_option("Inferno256") + # Change ordering of survey table. + page.get_by_role("columnheader", name="Reward", exact=True).locator("div").nth(4).click() + # Change ordering of bf table. + page.get_by_role("columnheader", name="Basis Function").locator("div").nth(3).click() + + # Allow the above changes time to propogate. + time.sleep(5) + + # Reset loading conditions. + page.get_by_role("button", name="﫽 Restore Loading Conditions").click() + + # Check loading indicator. + indicator_div = page.locator("div").filter(has_text="Scheduler Dashboard").nth(1) + expect(indicator_div).to_have_attribute("class", "bk-GridBox pn-loading pn-arc") + + # 4x info messages pop up. + expect(page.get_by_text("Scheduler loading...").first).to_be_visible() + expect(page.get_by_text("Scheduler pickle loaded successfully!").first).to_be_visible() + expect(page.get_by_text("Making scheduler summary dataframe...").first).to_be_visible() + expect(page.get_by_text("Scheduler summary dataframe updated successfully").first).to_be_visible() + + # Check subheading = ‘Tier 0 - Survey 0 - Map reward’. + expect(page.locator("pre").nth(1)).to_contain_text("Tier 0 - Survey 0 - Map reward") + # Check 3x headings return to tier 0, survey 0. + survey_name = page.get_by_role("row").nth(1).get_by_role("gridcell").nth(1).text_content() + expect(page.locator("pre").nth(2)).to_contain_text("Scheduler summary for tier 0") + expect(page.locator("pre").nth(3)).to_contain_text( + f"Basis functions & rewards for survey {survey_name}" + ) + expect(page.locator("pre").nth(4)).to_contain_text(f"Survey {survey_name}") + # Check date. (vulnerable to data changes) + expect(page.get_by_role("textbox")).to_have_value(ISO8601_REGEX) + # Check Survey Map shows ‘reward’. + expect(page.get_by_role("combobox").nth(2)).to_have_value("reward") + # Check Map resolution = 16. + expect(page.get_by_role("combobox").nth(3)).to_have_value("16") + # Check color palette = Viridis255. + expect(page.get_by_role("combobox").nth(4)).to_have_value("Viridis256") + + # Select tier 3. (vulnerable to data changes) + page.get_by_role("combobox").nth(1).select_option("tier 3") + + # Check ordering of bf table (Accum. order). + expect(page.get_by_role("columnheader", name="Reward", exact=True)).to_have_attribute( + "aria-sort", "none" + ) + # Check ordering of survey table (index). + expect(page.get_by_role("columnheader", name="Basis Function")).to_have_attribute("aria-sort", "none") + + # Check debugger for any errors caused during test actions. + expect(page.locator("pre").nth(6)).not_to_contain_text(re.compile(r"Traceback|Cannot|unable")) + # Close debugger. + page.get_by_role("button", name="Debugging").click() + + +@skip_playwright_tests +class TestURLModeE2E(unittest.TestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.playwright = sync_playwright().start() + if HEADLESS: + cls.browser = cls.playwright.chromium.launch(headless=True) + else: + cls.browser = cls.playwright.chromium.launch(headless=False, slow_mo=100) + + cls.dashboard_process = subprocess.Popen( + ["python", "schedview/app/scheduler_dashboard/scheduler_dashboard.py", "--data_from_urls"] + ) + # Allow the dashboard time to start. + time.sleep(10) + + @classmethod + def tearDownClass(cls): + super().tearDownClass() + cls.browser.close() + cls.playwright.stop() + + if hasattr(cls, "dashboard_process") and cls.dashboard_process: + cls.dashboard_process.terminate() + cls.dashboard_process.wait() + + def test_invalid_input(self): + page = self.browser.new_page() + page.goto(f"http://localhost:{PORT}/schedview-snapshot") + + # Input invalid url/filepath. + snapshot_input = page.get_by_role("textbox").first + snapshot_input.fill("invalid_url_or_filepath") + snapshot_input.press("Enter") + + # Check pop-up error message displayed. + expect(page.get_by_text("Cannot load scheduler from").first).to_be_visible() + + # Check error displayed in debugging panel. + expect(page.locator("pre").nth(6)).to_contain_text("ValueError: unknown url type") + + def test_with_data(self): + page = self.browser.new_page() + page.goto(f"http://localhost:{PORT}/schedview-snapshot") + + # Load data from pickle. + snapshot_input = page.get_by_role("textbox").first + snapshot_input.fill(TEST_PICKLE) + snapshot_input.press("Enter") + + # Check loading indicator. + indicator_div = page.locator("div").filter(has_text="Scheduler Dashboard").nth(1) + expect(indicator_div).to_have_attribute("class", "bk-GridBox pn-loading pn-arc") + + # Check 4x info messages displayed. + expect(page.get_by_text("Scheduler loading...").first).to_be_visible() + expect(page.get_by_text("Scheduler pickle loaded successfully!").first).to_be_visible() + expect(page.get_by_text("Making scheduler summary dataframe...").first).to_be_visible() + expect(page.get_by_text("Scheduler summary dataframe updated successfully").first).to_be_visible() + + # Check subheading. + expect(page.locator("pre").nth(1)).to_contain_text("Tier 0 - Survey 0 - Map reward") + # Check date. + expect(page.get_by_role("textbox").nth(1)).to_have_value(ISO8601_REGEX) + + # Select tier 5. + page.get_by_role("combobox").first.select_option("tier 5") + # Wait for update to complete. + page.wait_for_selector("text=Tier 5 - Survey 0 - Map reward") + # Select survey 1. + page.get_by_role("row").nth(2).click() + # Wait for update to complete. + page.wait_for_selector("text=Tier 5 - Survey 1 - Map reward") + + # Check 3x headings updated. + survey_name = page.get_by_role("row").nth(2).get_by_role("gridcell").nth(1).text_content() + expect(page.locator("pre").nth(2)).to_contain_text("Scheduler summary for tier 5") + expect(page.locator("pre").nth(3)).to_contain_text( + f"Basis functions & rewards for survey {survey_name}" + ) + expect(page.locator("pre").nth(4)).to_contain_text(f"Survey {survey_name} Map: reward") + # Check 2x tables visible. + expect(page.get_by_role("grid")).to_have_count(2) + # Check 1x map/key visible. + expect(page.locator(".bk-Canvas > div:nth-child(11)")).to_be_visible() + + # Check debugger for any errors caused during test actions. + expect(page.locator("pre").nth(6)).not_to_contain_text(re.compile(r"Traceback|Cannot|unable")) + + +@skip_playwright_tests +class TestLFAModeE2E(unittest.TestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.playwright = sync_playwright().start() + if HEADLESS: + cls.browser = cls.playwright.chromium.launch(headless=True) + else: + cls.browser = cls.playwright.chromium.launch(headless=False, slow_mo=100) + + cls.dashboard_process = subprocess.Popen( + ["python", "schedview/app/scheduler_dashboard/scheduler_dashboard.py", "--lfa"] + ) + # Allow the dashboard time to start. + time.sleep(10) + + @classmethod + def tearDownClass(cls): + super().tearDownClass() + cls.browser.close() + cls.playwright.stop() + + if hasattr(cls, "dashboard_process") and cls.dashboard_process: + cls.dashboard_process.terminate() + cls.dashboard_process.wait() + + def test_with_data(self): + page = self.browser.new_page() + page.goto(f"http://localhost:{PORT}/schedview-snapshot") + + # Choose snapshot date. + page.get_by_role("textbox").first.click() # open date picker + page.get_by_label("Month").first.select_option("2") # change date + page.get_by_label("March 12,").click() + page.get_by_label("Hour").first.fill("2") # change hour + page.get_by_label("Minute").first.fill("0") # change minute + page.get_by_role("spinbutton").nth(3).fill("0") # change second + page.get_by_text("::").first.press("Enter") # submit + + # Check loading indicator. + indicator_div = page.locator("div").filter(has_text="Scheduler Dashboard").nth(1) + expect(indicator_div).to_have_attribute("class", "bk-GridBox pn-loading pn-arc") + + # Check 4x info messages displayed. + expect(page.get_by_text("Loading snapshots...").first).to_be_visible() + expect(page.get_by_text("Snapshots loaded!!").first).to_be_visible() + + # Select telescope. + page.get_by_role("combobox").first.select_option("Auxtel") + # page.get_by_label("Telescope").select_option("Auxtel") + + # Check loading indicator. + indicator_div = page.locator("div").filter(has_text="Scheduler Dashboard").nth(1) + expect(indicator_div).to_have_attribute("class", "bk-GridBox") + + # Check 4x info messages displayed. + expect(page.get_by_text("Loading snapshots...").first).to_be_visible() + expect(page.get_by_text("Snapshots loaded!!").first).to_be_visible() + + # Select snapshot file. + snapshot = "s3://rubin:rubinobs-lfa-cp/Scheduler:2/Scheduler:2/2024/03/11/Scheduler:2_Scheduler:2_2024-03-12T00:33:05.127.p" + page.get_by_role("combobox").nth(1).select_option(snapshot) + + # Check loading indicator. + indicator_div = page.locator("div").filter(has_text="Scheduler Dashboard").nth(1) + expect(indicator_div).to_have_attribute("class", "bk-GridBox pn-loading pn-arc") + + # Check 4x info messages displayed. + expect(page.get_by_text("Scheduler loading...").first).to_be_visible() + expect(page.get_by_text("Scheduler pickle loaded successfully!").first).to_be_visible() + expect(page.get_by_text("Making scheduler summary dataframe...").first).to_be_visible() + expect(page.get_by_text("Scheduler summary dataframe updated successfully").first).to_be_visible() + + # Check 3x headings updated. + survey_name = page.get_by_role("row").nth(1).get_by_role("gridcell").nth(1).text_content() + expect(page.locator("pre").nth(2)).to_contain_text("Scheduler summary for tier 0") + expect(page.locator("pre").nth(3)).to_contain_text( + f"Basis functions & rewards for survey {survey_name}" + ) + expect(page.locator("pre").nth(4)).to_contain_text(f"Survey {survey_name} Map: reward") + # Check 2x tables visible. + expect(page.get_by_role("grid")).to_have_count(2) + # Check 1x map/key visible. + expect(page.locator(".bk-Canvas > div:nth-child(11)")).to_be_visible() + + # Check debugger for any errors caused during test actions. + expect(page.locator("pre").nth(6)).not_to_contain_text(re.compile(r"Traceback|Cannot|unable")) + # Close debugger. + page.get_by_role("button", name="Debugging").click() + + if __name__ == "__main__": unittest.main()