From c0ced4f1472100f9de8e773af9b6cd6e254ce1be Mon Sep 17 00:00:00 2001 From: Eric Gentry Date: Fri, 24 Feb 2023 13:41:28 -0500 Subject: [PATCH] CI Test Fixes Round 4 (#226) * Refactors to _dashboard_nav for added reliability. * Added docs/comments, code cleanup. * Remove timeout option for get_inner_text. --- nbclassic/tests/end_to_end/conftest.py | 6 ++ .../tests/end_to_end/test_dashboard_nav.py | 30 ++++++-- nbclassic/tests/end_to_end/utils.py | 74 ++++++++++++++----- 3 files changed, 86 insertions(+), 24 deletions(-) diff --git a/nbclassic/tests/end_to_end/conftest.py b/nbclassic/tests/end_to_end/conftest.py index a45309ffa..61961645c 100644 --- a/nbclassic/tests/end_to_end/conftest.py +++ b/nbclassic/tests/end_to_end/conftest.py @@ -86,6 +86,12 @@ def playwright_browser(playwright): break except Exception: time.sleep(.2) + else: + # You need to make sure the host machine has run `playwright install` + # to actually obtain the browser here/for this to work (you should be + # able to run the playwright binary after pip installing nbclassic + # with optional test extras) + raise Exception('Could not open browser! Did you `playwright install` on this machine?') yield browser diff --git a/nbclassic/tests/end_to_end/test_dashboard_nav.py b/nbclassic/tests/end_to_end/test_dashboard_nav.py index 558e04272..b391fa919 100644 --- a/nbclassic/tests/end_to_end/test_dashboard_nav.py +++ b/nbclassic/tests/end_to_end/test_dashboard_nav.py @@ -2,6 +2,7 @@ import os + from .utils import TREE_PAGE from jupyter_server.utils import url_path_join pjoin = os.path.join @@ -20,6 +21,7 @@ def get_list_items(nb): Gets list items from a directory listing page """ + nb.wait_for_selector('#notebook_list .item_link', page=TREE_PAGE) notebook_list = nb.locate('#notebook_list', page=TREE_PAGE) link_items = notebook_list.locate_all('.item_link') @@ -31,18 +33,35 @@ def get_list_items(nb): def test_navigation(notebook_frontend): - + print('[Test] [test_dashboard_nav] Start!') + + print('[Test] Obtain list of elements') link_elements = get_list_items(notebook_frontend) + # Recursively traverse and check folder in the Jupyter root dir def check_links(nb, list_of_link_elements): + print('[Test] Check links') if len(list_of_link_elements) < 1: - return False + return for item in list_of_link_elements: + print(f'[Test] Check "{item["label"]}"') + if 'Untitled.ipynb' in item["label"]: + # Skip notebook files in the temp dir + continue + item["element"].click() - assert url_in_tree(notebook_frontend) == True - assert item["link"] in nb.get_page_url(page=TREE_PAGE) + notebook_frontend.wait_for_condition( + lambda: url_in_tree(notebook_frontend), + timeout=600, + period=5 + ) + notebook_frontend.wait_for_condition( + lambda: item["link"] in nb.get_page_url(page=TREE_PAGE), + timeout=600, + period=5 + ) new_links = get_list_items(nb) if len(new_links) > 0: @@ -50,6 +69,7 @@ def check_links(nb, list_of_link_elements): nb.go_back(page=TREE_PAGE) - return + return check_links(notebook_frontend, link_elements) + print('[Test] [test_dashboard_nav] Finished!') diff --git a/nbclassic/tests/end_to_end/utils.py b/nbclassic/tests/end_to_end/utils.py index 9d14742a8..1e215cef6 100644 --- a/nbclassic/tests/end_to_end/utils.py +++ b/nbclassic/tests/end_to_end/utils.py @@ -31,7 +31,7 @@ BROWSER_OBJ = 'BROWSER_OBJ' # Other constants CELL_OUTPUT_SELECTOR = '.output_subarea' - +SECONDS_TO_MILLISECONDS = 1000 class EndToEndTimeout(Exception): @@ -100,8 +100,8 @@ def __bool__(self): # (Quick/dirty )We can debug on failures by deferring bad inits and testing for them here return self._bool - def click(self): - return self._element.click() + def click(self, timeout=30): + return self._element.click(timeout=timeout * SECONDS_TO_MILLISECONDS) def get_inner_text(self): return self._element.inner_text() @@ -174,9 +174,8 @@ def get_user_data(self): return self._user_data def expect_not_to_be_visible(self, timeout=30): - seconds_to_milliseconds = 1000 try: - expect(self._element).not_to_be_visible(timeout=timeout * seconds_to_milliseconds) + expect(self._element).not_to_be_visible(timeout=timeout * SECONDS_TO_MILLISECONDS) except ValueError as err: raise Exception('Cannot expect not_to_be_visible on this type!') from err except AssertionError as err: @@ -797,27 +796,64 @@ def is_kernel_running(self): def wait_for_kernel_ready(self): self._editor_page.wait_for_selector(".kernel_idle_icon") - def _open_notebook_editor_page(self, existing_file_name=None): + def get_editor_page_from_existing_notebook(self, existing_file_name): tree_page = self._tree_page - if existing_file_name is not None: - existing_notebook = tree_page.locator(f"text={existing_file_name}") - existing_notebook.click() - self._tree_page.reload() # TODO: FIX this, page count does not update to 2 - else: - # Simulate a user opening a new notebook/kernel - new_dropdown_element = tree_page.locator('#new-dropdown-button') - new_dropdown_element.click() - kernel_name = 'kernel-python3' - kernel_selector = f'#{kernel_name} a' - new_notebook_element = tree_page.locator(kernel_selector) - new_notebook_element.click() + # Find the link to the notebook file on the tree page + notebook_name_selector = f"text={existing_file_name}" + tree_page.wait_for_selector(notebook_name_selector) + # Click the existing notebook link on the tree page to open the editor + existing_notebook = tree_page.locator(notebook_name_selector) + existing_notebook.click() + self._tree_page.reload() # TODO: FIX this, page count does not update to 2 + + def wait_for_new_page(): + return [pg for pg in self._browser_data[BROWSER_CONTEXT].pages if 'tree' not in pg.url] + + new_pages = self.wait_for_condition( + wait_for_new_page, + timeout=125, + period=1 + ) + editor_page = new_pages[0] + + return editor_page + + def get_new_editor_page(self): + tree_page = self._tree_page + + # Simulate a user opening a new notebook/kernel + new_dropdown_element = tree_page.locator('#new-dropdown-button') + new_dropdown_element.click() + kernel_name = 'kernel-python3' + kernel_selector = f'#{kernel_name} a' + new_notebook_element = tree_page.locator(kernel_selector) + new_notebook_element.click() def wait_for_new_page(): return [pg for pg in self._browser_data[BROWSER_CONTEXT].pages if 'tree' not in pg.url] new_pages = self.wait_for_condition(wait_for_new_page) editor_page = new_pages[0] + + return editor_page + + def _open_notebook_editor_page(self, existing_file_name=None): + tree_page = self._tree_page + + if existing_file_name is not None: + editor_page = self.wait_for_condition( + lambda: self.get_editor_page_from_existing_notebook(existing_file_name), + timeout=500, + period=1 + ) + else: + editor_page = self.wait_for_condition( + lambda: self.get_new_editor_page(), + timeout=500, + period=1 + ) + return editor_page def get_page_url(self, page): @@ -829,7 +865,7 @@ def get_page_url(self, page): raise Exception('Error, provide a valid page to evaluate from!') return specified_page.url - + def go_back(self, page): if page == TREE_PAGE: specified_page = self._tree_page