|
4 | 4 | import time |
5 | 5 |
|
6 | 6 | import pytest |
| 7 | +from selenium.webdriver.support.wait import WebDriverWait |
7 | 8 |
|
8 | 9 | from sagemaker_ssh_helper.ide import SSHIDE |
9 | 10 | from sagemaker_ssh_helper.manager import SSMManager |
10 | 11 | from sagemaker_ssh_helper.proxy import SSMProxy |
11 | 12 |
|
| 13 | +from selenium import webdriver |
| 14 | + |
| 15 | +from selenium.webdriver.common.by import By |
| 16 | + |
| 17 | +import boto3 |
| 18 | + |
| 19 | +from selenium.webdriver.support import expected_conditions as EC |
| 20 | + |
12 | 21 | logger = logging.getLogger('sagemaker-ssh-helper:test_ide') |
13 | 22 |
|
14 | 23 |
|
@@ -225,3 +234,81 @@ def test_studio_internet_free_mode(request): |
225 | 234 | assert "127.0.0.1:5901" in services_running |
226 | 235 |
|
227 | 236 | ide.delete_kernel_app("byoi-studio-app", wait=False) |
| 237 | + |
| 238 | + |
| 239 | +@pytest.mark.skipif(os.getenv('PYTEST_IGNORE_SKIPS', "false") == "false", |
| 240 | + reason="Manual test") |
| 241 | +def test_studio_notebook_in_firefox(request): |
| 242 | + ide = SSHIDE(request.config.getini('sagemaker_studio_domain'), 'test-data-science') |
| 243 | + |
| 244 | + # Get SageMaker Studio Presigned URL with API |
| 245 | + sagemaker_client = boto3.client('sagemaker') |
| 246 | + studio_pre_signed_url_response = sagemaker_client.create_presigned_domain_url( |
| 247 | + DomainId=ide.domain_id, |
| 248 | + UserProfileName=ide.user, |
| 249 | + ) |
| 250 | + studio_pre_signed_url = studio_pre_signed_url_response['AuthorizedUrl'] |
| 251 | + logging.info(f"Studio pre-signed URL: {studio_pre_signed_url}") |
| 252 | + |
| 253 | + logging.info("Launching Firefox") |
| 254 | + browser = webdriver.Firefox() |
| 255 | + |
| 256 | + logging.info("Launching SageMaker Studio") |
| 257 | + browser.get(studio_pre_signed_url) |
| 258 | + |
| 259 | + logging.info("Checking for SageMaker Studio in Firefox") |
| 260 | + assert "JupyterLab" in browser.title |
| 261 | + |
| 262 | + logging.info("Waiting for SageMaker Studio to launch") |
| 263 | + kernel_menu_xpath = "//div[@class='lm-MenuBar-itemLabel p-MenuBar-itemLabel' " \ |
| 264 | + "and text()='Kernel']" |
| 265 | + WebDriverWait(browser, 30).until( |
| 266 | + EC.presence_of_element_located((By.XPATH, kernel_menu_xpath)) |
| 267 | + ) |
| 268 | + time.sleep(15) # wait until obscurity of the menu item is gone and UI is fully loaded |
| 269 | + kernel_menu_item = browser.find_element(By.XPATH, kernel_menu_xpath) |
| 270 | + logging.info(f"Found SageMaker Studio kernel menu item: {kernel_menu_item}") |
| 271 | + kernel_menu_item.click() |
| 272 | + |
| 273 | + logging.info("Restarting kernel and running all cells") |
| 274 | + restart_menu_xpath = "//div[@class='lm-Menu-itemLabel p-Menu-itemLabel' " \ |
| 275 | + "and text()='Restart Kernel and Run All Cells…']" |
| 276 | + restart_menu_item = browser.find_element(By.XPATH, restart_menu_xpath) |
| 277 | + logging.info(f"Found SageMaker Studio restart kernel menu item: {restart_menu_item}") |
| 278 | + restart_menu_item.click() |
| 279 | + |
| 280 | + # TODO: check if kernel has been already started, also check that it's a correct kernel and instance type |
| 281 | + # <button type="button" class="bp3-button bp3-minimal jp-Toolbar-kernelName jp-ToolbarButtonComponent minimal jp-Button" aria-disabled="false" title="Switch kernel"><span class="bp3-button-text"><span class="jp-ToolbarButtonComponent-label">No Kernel</span></span></button> |
| 282 | + # <button type="button" class="bp3-button bp3-minimal jp-Toolbar-kernelName jp-ToolbarButtonComponent minimal jp-Button" aria-disabled="false" title=""><span class="bp3-button-text"><span class="jp-ToolbarButtonComponent-label" style="display: none;">Python 3 (Data Science 2.0)</span></span><span class="css-1jyspix newButtonTarget"><span class="css-1vcsdgo">Data Science 2.0</span><span class="css-pyakce">|</span><span>Python 3</span><span class="css-pyakce">|</span><span>2 vCPU + 4 GiB</span></span></button> |
| 283 | + |
| 284 | + # TODO: check banner if kernel is still starting, wait until banner disappears, then click restart |
| 285 | + # <div class="css-a7sx0c-bannerContainer sagemaker-starting-banner" id="sagemaker-notebook-banner"><div class="css-1qyc1pu-kernelStartingBannerContainer"><div><div class="css-6wrpfe-bannerSpinDiv"></div></div><div><p class="css-g9mx5z-bannerPromptSpanTitle">Starting notebook kernel...</p></div></div></div> |
| 286 | + |
| 287 | + restart_button_xpath = "//div[@class='jp-Dialog-buttonLabel' " \ |
| 288 | + "and text()='Restart']" |
| 289 | + restart_button = browser.find_element(By.XPATH, restart_button_xpath) |
| 290 | + logging.info(f"Found SageMaker Studio restart button: {restart_button}") |
| 291 | + restart_button.click() |
| 292 | + |
| 293 | + time.sleep(120) # Give time to restart |
| 294 | + |
| 295 | + studio_ids = ide.get_kernel_instance_ids("sagemaker-data-science-ml-m5-large-6590da95dc67eec021b14bedc036", |
| 296 | + timeout_in_sec=300) |
| 297 | + studio_id = studio_ids[0] |
| 298 | + |
| 299 | + with SSMProxy(10022) as ssm_proxy: |
| 300 | + ssm_proxy.connect_to_ssm_instance(studio_id) |
| 301 | + services_running = ssm_proxy.run_command_with_output("sm-ssh-ide status") |
| 302 | + services_running = services_running.decode('latin1') |
| 303 | + |
| 304 | + assert "127.0.0.1:8889" in services_running |
| 305 | + assert "127.0.0.1:5901" in services_running |
| 306 | + |
| 307 | + # TODO: assert the services were restarted by the test, e.g., by checking the SSM timestamp |
| 308 | + # TODO: check if the second restart also successful |
| 309 | + |
| 310 | + # TODO: restart image (clean-up for the next run) |
| 311 | + # <button type="button" class="bp3-button bp3-minimal jp-ToolbarButtonComponent minimal jp-Button" aria-disabled="false" title="Shut down">...</button> """ |
| 312 | + |
| 313 | + logging.info("Closing Firefox") |
| 314 | + browser.close() |
0 commit comments