Skip to content

Commit

Permalink
Introduce support for the Jupyter/Elyra 4.
Browse files Browse the repository at this point in the history
These changes are required to support the Jupyter/Elyra 4.x IDE version
bump which is expected in the OOTB Workbench images 2024.2+. All changes
are done so that the backward compatibility with the current
Jupyter/Elyra 3.x version is preserved.

Note: since the 'default' version of the workbench images still points
to 2024a with Jupyter 3, the line with the global env variable update to
work with Jupyter 4 is commented out now with a TODO comment.

Also, the reason why we are adding some code from the JupyterLibrary is
that we're trying to use SeleniumLibrary for the browser opening. This
doesn't match with some part of the code of the JupyterLibrary where it
is expected that some keywords that are present in both (SeleniumLibrary
and JupyterLibrary) are used from the JupyterLibrary. Since to narrow
down this discrepancy (using JupyterLibrary explicitly and completely
for the JupyterHub testing) would require more changes, I did it via
this ughly hack now having part of the source from the JupyterLibrary
directly in our codebase.
Truth is that JupyterLibrary project seems to be abandonded so we may
also think about fork it and handle it by ourselves in case we'll need
more changes there.
  • Loading branch information
jstourac committed Sep 27, 2024
1 parent 13aebed commit 7d509b8
Show file tree
Hide file tree
Showing 8 changed files with 1,533 additions and 1,379 deletions.
2 changes: 1 addition & 1 deletion ods_ci/tests/Resources/Common.robot
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Begin Web Test
End Web Test
[Arguments] ${username}=${TEST_USER.USERNAME}
${server}= Run Keyword And Return Status Page Should Contain Element
... //div[@id='jp-top-panel']//div[contains(@class, 'p-MenuBar-itemLabel')][text() = 'File']
... //div[@id='jp-top-panel']//div[contains(@class, '-MenuBar-itemLabel')][text() = 'File']
IF ${server}==True
Clean Up Server username=${username}
Stop JupyterLab Notebook Server
Expand Down
3 changes: 2 additions & 1 deletion ods_ci/tests/Resources/Page/ODH/JupyterHub/Elyra.resource
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ Run Pipeline
Click Run Button
Set Pipeline Name ${pipeline_name}
Select Runtime Config ${runtime_config}
Click Element xpath=//button[.="OK"]
# "OK" is for Elyra 3 ; "Ok" is for Elyra 4
Click Element xpath=//button[.="Ok" or .="OK"]

Click Run Button
[Documentation] Click the `Run` button in Elyra
Expand Down
56 changes: 56 additions & 0 deletions ods_ci/tests/Resources/Page/ODH/JupyterHub/Icons.resource
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
*** Comments ***
# This whole file is a copy of the "clients/jupyterlab/Icons.resource" file from the JupyterLibrary.

# Motivation for the copy is that there is an explicit usage of the JupyterLibrary on one line of
# the PageInfo.resource file in that library. So this file and the PageInfo.resource file were moved
# to our repository with the one line fix.


*** Settings ***
Documentation Icon keywords for JupyterLab
# Resource JupyterLibrary/clients/jupyterlab/PageInfo.resource
Resource ./PageInfo.resource # Edited - our change


*** Variables ***
&{JLAB1 CSS ICONS}
... add=span[data-icon="add"]
... run=span[data-icon="run"]
... filled circle=.jp-CircleFilledIcon
... empty circle=.jp-CircleEmptyIcon
&{JLAB2 CSS ICONS}
... add=svg[@data-icon='ui-components:add']
... run=svg[@data-icon='ui-components:run']
... filled circle=svg[@data-icon='ui-components:circle-filled']
... empty circle=svg[@data-icon='ui-components:circle-empty']
&{JLAB1 XP ICONS}
... add=span[@data-icon = 'add']
... run=span[@data-icon = 'run']
... filled circle=div[contains(@class, 'jp-CircleFilledIcon')]
... empty circle=div[contains(@class, 'jp-CircleEmptyIcon')]
&{JLAB2 XP ICONS}
... add=*[@data-icon='ui-components:add']
... run=*[@data-icon='ui-components:run']
... filled circle=*[@data-icon='ui-components:circle-filled']
... empty circle=*[@data-icon='ui-components:circle-empty']


*** Keywords ***
Get JupyterLab Icon CSS Custom # Edited
[Documentation] Get a Lab version-specific, but general CSS selector for an ``icon``.
[Arguments] ${icon}
${version} = Get JupyterLab Application Version Info Custom
${sel} = Set Variable If ${version[0].__eq__('1')}
... ${JLAB1 CSS ICONS['${icon}']}
... ${JLAB2 CSS ICONS['${icon}']}
[Return] ${sel}

Get JupyterLab Icon XPath Custom # Edited
[Documentation] Get a Lab version-specific, but general XPath selector for an ``icon``.
[Arguments] ${icon}
${version} = Get JupyterLab Application Version Info Custom
${sel} = Set Variable If ${version[0].__eq__('1')}
... ${JLAB1 XP ICONS['${icon}']}
... ${JLAB2 XP ICONS['${icon}']}
[Return] ${sel}
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ Wait Notebook To Be Loaded
Open New Notebook In Jupyterlab Menu
Spawned Image Check ${image} ${version}
ELSE
Fail msg=Unknown IDE typ has been resolved: '${ide}'. Please check and fix or implement.
Fail msg=Unknown IDE type has been resolved: '${ide}'. Please check and fix or implement.
END

Spawn Notebook With Arguments # robocop: disable
Expand Down Expand Up @@ -354,6 +354,12 @@ Spawn Notebook With Arguments # robocop: disable
Run Keyword And Warn On Failure Login To Openshift ${username} ${password} ${auth_type}
${authorization_required} = Is Service Account Authorization Required
IF ${authorization_required} Authorize jupyterhub service account

# For Jupyter 4, we need to update global default variable values (images 2024b and newer)
# This calls method from JupyterLibrary Version.resource module
# TODO - shall be uncommented once the 2024b images will land into the product
# IF "${version}"=="default" Update Globals For JupyterLab 4

Wait Notebook To Be Loaded ${image} ${version}
${spawn_fail} = Has Spawn Failed
Exit For Loop If ${spawn_fail} == False
Expand Down
85 changes: 69 additions & 16 deletions ods_ci/tests/Resources/Page/ODH/JupyterHub/JupyterLabLauncher.robot
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
*** Settings ***
Resource ../../OCPDashboard/Pods/Pods.robot
Resource ./Icons.resource

Library JupyterLibrary
Library jupyter-helper.py
Library OperatingSystem
Expand All @@ -10,17 +12,14 @@ Library SeleniumLibrary


*** Variables ***
${JL_TABBAR_CONTENT_XPATH} = //div[contains(@class,"lm-DockPanel-tabBar")]/ul[@class="lm-TabBar-content p-TabBar-content"]
${JL_TABBAR_SELECTED_XPATH} = ${JL_TABBAR_CONTENT_XPATH}/li[contains(@class,"lm-mod-current p-mod-current")]
${JL_TABBAR_NOT_SELECTED_XPATH} = ${JL_TABBAR_CONTENT_XPATH}/li[not(contains(@class,"lm-mod-current p-mod-current"))]
${JL_TABBAR_CONTENT_XPATH} //div[contains(@class,"lm-DockPanel-tabBar")]
${JL_TABBAR_SELECTED_XPATH} ${JL_TABBAR_CONTENT_XPATH}//li[@aria-selected="true"]
${JL_TABBAR_NOT_SELECTED_XPATH} ${JL_TABBAR_CONTENT_XPATH}//li[@aria-selected="false"]
${JLAB CSS ACTIVE DOC} .jp-Document:not(.jp-mod-hidden)
${JLAB CSS ACTIVE CELL} ${JLAB CSS ACTIVE DOC} .jp-Cell.jp-mod-active
${JLAB CSS ACTIVE INPUT} ${JLAB CSS ACTIVE CELL} .CodeMirror
${JLAB XP NB TOOLBAR FRAG} [contains(@class, 'jp-NotebookPanel-toolbar')]
${JLAB CSS ACTIVE DOC} .jp-Document:not(.jp-mod-hidden)
${JLAB CSS ACTIVE DOC CELLS} ${JLAB CSS ACTIVE DOC} .jp-Cell
${JLAB CSS ACTIVE CELL} ${JLAB CSS ACTIVE DOC} .jp-Cell.jp-mod-active
${JLAB CSS ACTIVE INPUT} ${JLAB CSS ACTIVE CELL} .CodeMirror
${REPO_URL} https://github.com/sclorg/nodejs-ex.git
${FILE_NAME} nodejs-ex

Expand All @@ -30,23 +29,22 @@ Get JupyterLab Selected Tab Label
RETURN ${tab_label}

JupyterLab Launcher Tab Is Visible
Get WebElement xpath:${JL_TABBAR_CONTENT_XPATH}/li/div[.="Launcher"]
Get WebElement xpath:${JL_TABBAR_CONTENT_XPATH}//li/div[.="Launcher"]

JupyterLab Launcher Tab Is Selected
Get WebElement xpath:${JL_TABBAR_SELECTED_XPATH}/div[.="Launcher"]

Open JupyterLab Launcher
Maybe Select Kernel
Open With JupyterLab Menu File New Launcher
Click Element //div[@title="New Launcher"]
JupyterLab Launcher Tab Is Visible
JupyterLab Launcher Tab Is Selected

Wait Until ${filename} JupyterLab Tab Is Selected
Wait Until Page Contains Element xpath:${JL_TABBAR_SELECTED_XPATH}/div[.="${filename}"]

Close Other JupyterLab Tabs
${original_tab} = Get WebElement xpath:${JL_TABBAR_SELECTED_XPATH}/div[contains(@class, "p-TabBar-tabLabel")]
#${original_tab} = Get WebElement xpath:${JL_TABBAR_SELECTED_XPATH}/div[contains(concat(' ',normalize-space(@class),' '),' p-TabBar-tabLabel ')]
${original_tab} = Get WebElement xpath:${JL_TABBAR_SELECTED_XPATH}/div[contains(@class, "-TabBar-tabLabel")]

${xpath_background_tab} = Set Variable xpath:${JL_TABBAR_NOT_SELECTED_XPATH}
${jl_tabs} = Get WebElements ${xpath_background_tab}
Expand Down Expand Up @@ -329,12 +327,29 @@ Maybe Close Popup
Capture Page Screenshot
END

Add And Run JupyterLab Code Cell In Active Notebook # robocop:disable
Add And Run JupyterLab Code Cell In Active Notebook
[Arguments] @{code} ${n}=1
[Documentation] Add a ``code`` cell to the ``n`` th notebook on the page and run it.
... ``code`` is a list of strings to set as lines in the code editor.
... ``n`` is the 1-based index of the notebook, usually in order of opening.
${add icon} = Get JupyterLab Icon XPath add
... This keyword should work on both JupyterLab 3.x and JupyterLab 4.x
# JupyterLab 3.x uses CodeMirror 5; JupyterLab 4.x uses CodeMirror 6
# CM VERSION variable is set via JupyterLibrary - we have to run
# Update Globals For JupyterLab 4 keyword in case we're running JupyterLab 4.x image.
IF "${CM VERSION}"=="6"
Add And Run JupyterLab Code Cell 6 In Active Notebook @{code} n=${n}
ELSE
Add And Run JupyterLab Code Cell 5 In Active Notebook @{code} n=${n}
END

Add And Run JupyterLab Code Cell 5 In Active Notebook
[Documentation] Add a ``code`` cell to the ``n`` th notebook on the page and run it.
... ``code`` is a list of strings to set as lines in the code editor.
... ``n`` is the 1-based index of the notebook, usually in order of opening.
[Arguments] @{code} ${n}=1
# This keyword was copied and amended from JupyterLibrary resources - Notebook.Add And Run JupyterLab Code Cell.

${add icon} = Get JupyterLab Icon XPath Custom add

${nb} = Get WebElement xpath://div${JLAB XP NB FRAG}\[${n}]
${nbid} = Get Element Attribute ${nb} id
Expand All @@ -346,17 +361,55 @@ Add And Run JupyterLab Code Cell In Active Notebook # robocop:disable
Sleep 0.1s
Click Element xpath://div[@aria-labelledby="${tab-id}"]//div[contains(concat(' ',normalize-space(@class),' '),' jp-mod-selected ')]
Set CodeMirror Value \#${nbid}${JLAB CSS ACTIVE INPUT} @{code}
Run Current JupyterLab Code Cell MOD ${tab-id}
Run Current JupyterLab Code Cell 5 ${tab-id}
Click Element xpath://div[@aria-labelledby="${tab-id}"]//div[contains(concat(' ',normalize-space(@class),' '),' jp-mod-selected ')]

Run Current JupyterLab Code Cell MOD
[Arguments] ${tab-id}
Add And Run JupyterLab Code Cell 6 In Active Notebook
[Documentation] Add a ``code`` cell to the ``n`` th notebook on the page and run it.
... ``code`` is a list of strings to set as lines in the code editor.
... ``n`` is the 1-based index of the notebook, usually in order of opening.
# This keyword was copied and amended from JupyterLibrary resources - Notebook.Add And Run JupyterLab Code Cell.

[Arguments] @{code} ${n}=1
${add icon} = Get JupyterLab Icon XPath Custom add
${nb} = Get WebElement xpath://div${JLAB XP NB FRAG}\[${n}]
# rely on main area widgets all having ids
${nbid} = JupyterLibrary.Get Element Attribute ${nb} id
${icon} = Get WebElement Relative To ${nb}
... xpath://*[@aria-label="notebook actions"]//*[contains(@class, "jp-CommandToolbarButton") and .//${add icon}]
Click Element ${icon}
Sleep 0.1s
${cell} = Get WebElement Relative To ${nb}
... css:${JLAB CSS ACTIVE INPUT.replace('''${JLAB CSS ACTIVE DOC}''', '')}
Click Element ${cell}
Set CodeMirror Value \#${nbid}${JLAB CSS ACTIVE INPUT} @{code}
Run Current JupyterLab Code Cell 6 ${n}
Click Element ${cell}

Run Current JupyterLab Code Cell 5
[Documentation] Run the currently-selected cell(s) in the ``n`` th notebook.
... ``n`` is the 1-based index of the notebook, usually in order of opening.
${run icon} = Get JupyterLab Icon XPath run
[Arguments] ${tab-id}
# This keyword was copied and amended from JupyterLibrary resources - Notebook.Run Current JupyterLab Code Cell

${run icon} = Get JupyterLab Icon XPath Custom run
Click Element xpath://div[@aria-labelledby="${tab-id}"]/div[1]//${run icon}
Sleep 0.5s

Run Current JupyterLab Code Cell 6
[Documentation] Run the currently-selected cell(s) in the ``n`` th notebook.
... ``n`` is the 1-based index of the notebook, usually in order of opening.
[Arguments] ${n}=1
# This keyword was copied and amended from JupyterLibrary resources - Notebook.Run Current JupyterLab Code Cell

${run icon} = Get JupyterLab Icon XPath Custom run
${nb} = Get WebElement xpath://div${JLAB XP NB FRAG}\[${n}]
# ${run btn} = Get WebElement Relative To ${nb} xpath:div${JLAB XP NB TOOLBAR FRAG}//${run icon}
${run btn} = Get WebElement Relative To ${nb}
... xpath://*[@aria-label="notebook actions"]//*[contains(@class, "jp-CommandToolbarButton") and .//${run icon}] # Edited
Click Element ${run btn}
Sleep 0.5s

Wait Until JupyterLab Code Cell Is Not Active In a Given Tab
[Documentation] Waits until the current cell no longer has an active prompt "[*]:".
... This assumes that there is only one cell currently active and it is the currently selected cell
Expand Down
69 changes: 69 additions & 0 deletions ods_ci/tests/Resources/Page/ODH/JupyterHub/PageInfo.resource
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
*** Comments ***
# This whole file is a copy of the "clients/jupyterlab/PageInfo.resource" file from the JupyterLibrary.

# Motivation for the copy is that there is an explicit usage of the JupyterLibrary on one line of
# the PageInfo.resource file in that library. So this file and the Icons.resource file were moved
# to our repository with the one line fix. See the "Edited - our fix" line.


*** Settings ***
Documentation Page config keywords for JupyterLab
Resource JupyterLibrary/clients/jupyterlab/Selectors.resource
Library String
Library json WITH NAME JSON


*** Variables ***
${JLAB XP PAGEINFO} script[contains(@id, 'jupyter-config-data')]
# keep this updated below
@{JLAB DEFAULT PAGEINFO TAGS} appName appVersion buildAvailable
... buildCheck notebookVersion devMode


*** Keywords ***
Get JupyterLab Page Info Custom # Edited
[Documentation] Get one (or all) of the ``pageInfo`` ``key`` s from JupyterLab's HTML ``<head>``.
... Optionally ``clear`` the cached info first.
...
... See also:
... - [#Tag With JupyterLab Metadata|Tag With JupyterLab Metadata]
[Arguments] ${key}=${EMPTY} ${clear}=${False}
${pageInfo} = Get Variable Value ${JLAB PAGEINFO CACHE} ${EMPTY}
# Run Keyword If ${clear} or not ${pageInfo.__len__()} Update JupyterLab PageInfo Cache
Run Keyword If ${clear} or not ${pageInfo.__len__()} Update JupyterLab PageInfo Cache Custom # Edited
${pageInfo} = Set Variable ${JLAB PAGEINFO CACHE}
${result} = Set Variable If ${key.__len__()} ${pageInfo.get("${key}")} ${pageInfo}
[Return] ${result}

Update JupyterLab PageInfo Cache Custom # Edited
[Documentation] Update the cached JupyterLab ``pageInfo``. _Not usually needed._
${sel} = Set Variable xpath://${JLAB XP PAGEINFO}
Wait Until Page Contains Element ${sel}
# ${txt} = JupyterLibrary.Get Element Attribute ${sel} innerHTML
${txt} = Get Element Attribute ${sel} innerHTML # Edited - our fix
${pageInfo} = JSON.Loads ${txt}
Set Suite Variable ${JLAB PAGEINFO CACHE} ${pageInfo} children=${True}

Tag With JupyterLab Metadata Custom # Edited
[Documentation] Tag the current test (or suite) with ``keys`` from the
... JupyterLab ``pageInfo``.
... The default ``keys``: ``appName`` ``appVersion`` ``buildAvailable``
... ``buildCheck`` ``notebookVersion`` ``devMode``
[Arguments] ${keys}=${JLAB DEFAULT PAGEINFO TAGS} ${clear}=${False}
# ${info} = Get JupyterLab Page Info clear=${clear}
${info} = Get JupyterLab Page Info Custom clear=${clear} # Edited
FOR ${key} IN @{keys}
${val} = Set Variable ${info.get("${key}")}
Set Tags jupyterlab:${key}:${val}
END

Get JupyterLab Application Version Info Custom # Edited
[Documentation] Get the version of the application ``2.3.0.rc1`` as a list of
... strings from ``pageInfo``, e.g. ``["2", "3", "1", "rc1"]``. Optionally ``clear``
... the cached info first.
[Arguments] ${clear}=${False}
# ${version} = Get JupyterLab Page Info appVersion clear=${clear}
${version} = Get JupyterLab Page Info Custom appVersion clear=${clear} # Edited
${version_info} = Set Variable ${version.split(".")}
[Return] ${version_info}
Loading

0 comments on commit 7d509b8

Please sign in to comment.