Skip to content

Commit

Permalink
add jupyter lab 3.6.5 to tests
Browse files Browse the repository at this point in the history
Tests take now a JUPYTER_TYPE that determines the if executed with
jupyter lab or or notebook. By that the class names are determined.
Previous tests can be now execued with the tox environment
tests-notebook-7, the jupyter lab tests with tests-lab-3 tests. Tests
have been extended where they needed to be to also work with jupyter
lab.
  • Loading branch information
agoscinski committed Dec 22, 2023
1 parent 3537cad commit 991aa33
Show file tree
Hide file tree
Showing 5 changed files with 372 additions and 109 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Tests
name: Tests - lab 3

on:
push:
Expand Down
38 changes: 38 additions & 0 deletions .github/workflows/tests-notebook-7.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Tests - notebook-7

on:
push:
branches: [main]
pull_request:
# Check all PR

jobs:
tests:
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-22.04
python-version: "3.11"

steps:
- uses: actions/checkout@v3
- name: Install Firefox
uses: browser-actions/setup-firefox@latest

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- run: pip install tox

- name: run Python tests
run: tox -e tests-notebook-7

- name: run Python tests for coverage
run: tox -e coverage
- uses: codecov/codecov-action@v3
with:
files: coverage.xml
verbose: true

129 changes: 114 additions & 15 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,48 @@
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait

# Can be "notebook" for jupyter notebook or "lab" for jupyter lab
JUPYTER_TYPE = os.environ["JUPYTER_TYPE"] if "JUPYTER_TYPE" in os.environ else "lab"
# Because for the tests frontend elements are retrieved by class names which change
# between versions, these veriable are change depending on the version. The version is
# automatically determined on initialization of tests
JUPYTER_VERSION = None


def get_jupyter_version() -> str:
"""
Function so we can update the jupyter version during initialization
and use it in other files
"""
global JUPYTER_VERSION
if JUPYTER_VERSION is None:
raise ValueError("JUPYTER_VERSION was not correctly on initialization")
return JUPYTER_VERSION


@pytest.fixture(scope="session")
def notebook_service():
global JUPYTER_VERSION

if JUPYTER_TYPE not in ["lab", "notebook"]:
raise ValueError(
f"Tests do not support jupyter type {JUPYTER_TYPE!r}. Please use"
" 'notebook' or 'lab'."
)

# some hard coded port and token
port = 8815
token = "fe47337ccb5b331e3d26a36b92112664af06462e511f66bb"
jupyter_version = subprocess.check_output(
["jupyter", f"{JUPYTER_TYPE}", "--version"]
)
# convert to string
JUPYTER_VERSION = jupyter_version.decode().replace("\n", "")

jupyter_process = subprocess.Popen(
[
"jupyter",
"notebook",
f"{JUPYTER_TYPE}",
f"--NotebookApp.token={token}",
"--no-browser",
f"--port={port}",
Expand All @@ -33,7 +65,7 @@ def notebook_service():
yield url, token

# teardown juypter notebook
os.system(f"jupyter notebook stop {port}")
os.system(f"jupyter {JUPYTER_TYPE} stop {port}")
time.sleep(2)
os.system(f"kill {jupyter_process.pid}")

Expand All @@ -48,66 +80,133 @@ def _selenium_driver(nb_path):
"""
:param nb_path: jupyter notebook path
"""
global JUPYTER_TYPE
url, token = notebook_service
url_with_token = urljoin(url, f"tree/{nb_path}?token={token}")

if JUPYTER_TYPE == "lab":
nb_path_prefix = "lab/tree"
elif JUPYTER_TYPE == "notebook":
nb_path_prefix = "tree"
else:
raise ValueError(
f"Tests do not support jupyter type {JUPYTER_TYPE!r}. Please use"
" 'notebook' or 'lab'."
)

# nb_path_prefix =
url_with_token = urljoin(url, f"{nb_path_prefix}/{nb_path}?token={token}")
selenium.get(f"{url_with_token}")
selenium.implicitly_wait(10)
window_width = 1280
window_height = 1024
selenium.set_window_size(window_width, window_height)

# Click on restart kernel button
# ------------------------------

# jupyter lab < 4
if JUPYTER_TYPE == "lab":
if get_jupyter_version() < "4.0.0":
restart_kernel_button_class_name = (
"bp3-button.bp3-minimal.jp-ToolbarButtonComponent.minimal.jp-Button"
)
restart_kernel_button_title_attribute = (
"Restart Kernel and Run All Cells…"
)
else:
raise ValueError("jupyter lab > 4.0.0 is not supported.")
elif JUPYTER_TYPE == "notebook":
if get_jupyter_version() < "7.0.0":
restart_kernel_button_class_name = "btn.btn-default"
restart_kernel_button_title_attribute = (
"restart the kernel, then re-run the whole notebook (with dialog)"
)
else:
restart_kernel_button_class_name = (
"jp-ToolbarButtonComponent.jp-mod-minimal.jp-Button"
)
restart_kernel_button_title_attribute = (
"Restart the kernel and run all cells"
)

# the code below imitates this code which cannot find the button
# I think it is because the button is hidden by another element
# WebDriverWait(driver, 5).until(
# expected_conditions.text_to_be_present_in_element_attribute(
# (By.CLASS_NAME, "jp-ToolbarButtonComponent.jp-mod-minimal.jp-Button"),
# (By.CLASS_NAME, restart_kernel_button_class_name),
# "title",
# "Restart the kernel and run all cells"
# restart_kernel_button_title_attribute
# )
# )
restart_kernel_button = None
waiting_time = 10
start = time.time()

while restart_kernel_button is None and time.time() - start < waiting_time:
# does not work for older notebook versions
buttons = selenium.find_elements(
By.CLASS_NAME, "jp-ToolbarButtonComponent.jp-mod-minimal.jp-Button"
By.CLASS_NAME, restart_kernel_button_class_name
)
for button in buttons:
try:
title = button.get_attribute("title")
if title == "Restart the kernel and run all cells":
if (
button.is_displayed()
and title == restart_kernel_button_title_attribute
):
restart_kernel_button = button
except StaleElementReferenceException:
# element is not ready, go sleep
continue

time.sleep(0.1)
if restart_kernel_button is None:
raise ValueError('"Restart the kernel and run all cells" button not found.')
raise ValueError(
f"{restart_kernel_button_title_attribute!r} button not found."
)
restart_kernel_button.click()

# Click on confirm restart dialog
# -------------------------------

if JUPYTER_TYPE == "lab":
if get_jupyter_version() < "4.0.0":
restart_button_class_name = (
"jp-Dialog-button.jp-mod-accept.jp-mod-warn.jp-mod-styled"
)
restart_button_text = "Restart"
else:
raise ValueError("jupyter lab > 4.0.0 is not supported.")
elif JUPYTER_TYPE == "notebook":
if get_jupyter_version() < "7.0.0":
restart_button_class_name = "btn.btn-default.btn-sm.btn-danger"
restart_button_text = "Restart and Run All Cells"
else:
restart_button_class_name = "jp-Dialog-buttonLabel"
restart_button_text = "Restart"

# the code below imitates this code which cannot find the button
# I think it is because the button is hidden by another element
# WebDriverWait(driver, 5).until(
# expected_conditions.text_to_be_present_in_element(
# (By.CLASS_NAME, "jp-Dialog-buttonLabel"),
# "Restart"
# (By.CLASS_NAME, restart_button_class_name),
# restart_button_text
# )
# )

restart_button = None
waiting_time = 10
start = time.time()
while restart_button is None and time.time() - start < waiting_time:
buttons = selenium.find_elements(By.CLASS_NAME, "jp-Dialog-buttonLabel")
buttons = selenium.find_elements(By.CLASS_NAME, restart_button_class_name)
for button in buttons:
if button.text == "Restart":
if button.text == restart_button_text:
restart_button = button
time.sleep(0.1)

if restart_button is None:
raise ValueError('"Restart" button not found.')
raise ValueError(f"{restart_button_text!r} button not found.")
restart_button.click()

# wait until everything has been run
WebDriverWait(selenium, 10).until(
expected_conditions.text_to_be_present_in_element_attribute(
(By.CLASS_NAME, "jp-Notebook-ExecutionIndicator"), "data-status", "idle"
Expand Down
Loading

0 comments on commit 991aa33

Please sign in to comment.