Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Playwright port #139

Merged
merged 3 commits into from
Oct 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
161 changes: 161 additions & 0 deletions nbclassic/tests/end_to_end/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import os
import json
import sys
import time
from os.path import join as pjoin
from subprocess import Popen
from tempfile import mkstemp
from urllib.parse import urljoin

import pytest
import requests
from testpath.tempdir import TemporaryDirectory
# from selenium.webdriver import Firefox, Remote, Chrome

import nbformat
from nbformat.v4 import new_notebook, new_code_cell
from .utils import NotebookFrontend


def _wait_for_server(proc, info_file_path):
"""Wait 30 seconds for the notebook server to start"""
for i in range(300):
if proc.poll() is not None:
raise RuntimeError("Notebook server failed to start")
if os.path.exists(info_file_path):
try:
with open(info_file_path) as f:
return json.load(f)
except ValueError:
# If the server is halfway through writing the file, we may
# get invalid JSON; it should be ready next iteration.
pass
time.sleep(0.1)
raise RuntimeError("Didn't find %s in 30 seconds", info_file_path)


@pytest.fixture(scope='session')
def notebook_server():
info = {}
with TemporaryDirectory() as td:
nbdir = info['nbdir'] = pjoin(td, 'notebooks')
os.makedirs(pjoin(nbdir, 'sub ∂ir1', 'sub ∂ir 1a'))
os.makedirs(pjoin(nbdir, 'sub ∂ir2', 'sub ∂ir 1b'))

info['extra_env'] = {
'JUPYTER_CONFIG_DIR': pjoin(td, 'jupyter_config'),
'JUPYTER_RUNTIME_DIR': pjoin(td, 'jupyter_runtime'),
'IPYTHONDIR': pjoin(td, 'ipython'),
}
env = os.environ.copy()
env.update(info['extra_env'])

command = [sys.executable, '-m', 'nbclassic',
'--no-browser',
'--notebook-dir', nbdir,
# run with a base URL that would be escaped,
# to test that we don't double-escape URLs
'--ServerApp.base_url=/a@b/',
]
print("command=", command)
proc = info['popen'] = Popen(command, cwd=nbdir, env=env)
info_file_path = pjoin(td, 'jupyter_runtime',
f'jpserver-{proc.pid:d}.json')
info.update(_wait_for_server(proc, info_file_path))

print("Notebook server info:", info)
yield info

# Shut the server down
requests.post(urljoin(info['url'], 'api/shutdown'),
headers={'Authorization': 'token '+info['token']})


# def make_sauce_driver():
# """This function helps travis create a driver on Sauce Labs.
#
# This function will err if used without specifying the variables expected
# in that context.
# """
#
# username = os.environ["SAUCE_USERNAME"]
# access_key = os.environ["SAUCE_ACCESS_KEY"]
# capabilities = {
# "tunnel-identifier": os.environ["TRAVIS_JOB_NUMBER"],
# "build": os.environ["TRAVIS_BUILD_NUMBER"],
# "tags": [os.environ['TRAVIS_PYTHON_VERSION'], 'CI'],
# "platform": "Windows 10",
# "browserName": os.environ['JUPYTER_TEST_BROWSER'],
# "version": "latest",
# }
# if capabilities['browserName'] == 'firefox':
# # Attempt to work around issue where browser loses authentication
# capabilities['version'] = '57.0'
# hub_url = f"{username}:{access_key}@localhost:4445"
# print("Connecting remote driver on Sauce Labs")
# driver = Remote(desired_capabilities=capabilities,
# command_executor=f"http://{hub_url}/wd/hub")
# return driver


@pytest.fixture(scope='session')
def playwright_driver(playwright):
# TODO: Fix
# if os.environ.get('SAUCE_USERNAME'):
# driver = make_sauce_driver()
if os.environ.get('JUPYTER_TEST_BROWSER') == 'chrome':
driver = playwright.chromium.launch()
else:
driver = playwright.firefox.launch()
driver = driver.new_context().new_page()

yield driver

# # Teardown
# driver.quit()


# @pytest.fixture(scope='module')
# def authenticated_browser(selenium_driver, notebook_server):
# selenium_driver.jupyter_server_info = notebook_server
# selenium_driver.get("{url}?token={token}".format(**notebook_server))
# return selenium_driver
#
#
# @pytest.fixture
# def notebook(authenticated_browser):
# tree_wh = authenticated_browser.current_window_handle
# yield Notebook.new_notebook(authenticated_browser)
# authenticated_browser.switch_to.window(tree_wh)


@pytest.fixture(scope='module')
def authenticated_browser(playwright_driver, notebook_server):
playwright_driver.jupyter_server_info = notebook_server
playwright_driver.goto("{url}?token={token}".format(**notebook_server))
return playwright_driver


@pytest.fixture
def notebook(authenticated_browser):
# tree_wh = authenticated_browser.current_window_handle
yield NotebookFrontend.new_notebook(authenticated_browser)
# authenticated_browser.switch_to.window(tree_wh)


# @pytest.fixture
# def prefill_notebook(selenium_driver, notebook_server):
# def inner(cells):
# cells = [new_code_cell(c) if isinstance(c, str) else c
# for c in cells]
# nb = new_notebook(cells=cells)
# fd, path = mkstemp(dir=notebook_server['nbdir'], suffix='.ipynb')
# with open(fd, 'w', encoding='utf-8') as f:
# nbformat.write(nb, f)
# fname = os.path.basename(path)
# selenium_driver.get(
# "{url}notebooks/{}?token={token}".format(fname, **notebook_server)
# )
# return Notebook(selenium_driver)
#
# return inner
71 changes: 71 additions & 0 deletions nbclassic/tests/end_to_end/test_playwright.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"""Proof of concept for playwright testing, uses a reimplementation of test_execute_code"""


from playwright.sync_api import Page, expect

# TODO: Remove
# from selenium.webdriver.common.keys import Keys
# from .utils import shift, cmdtrl


def test_execute_code(notebook):
# browser = notebook.browser

# def clear_outputs():
# return notebook.evaluate(
# "Jupyter.notebook.clear_all_output();")

# Execute cell with Javascript API
notebook.edit_cell(index=0, content='a=10; print(a)')
notebook.evaluate("Jupyter.notebook.get_cell(0).execute();")
outputs = notebook.wait_for_cell_output(0)
assert outputs[0].text == '10'

# # Execute cell with Shift-Enter
# notebook.edit_cell(index=0, content='a=11; print(a)')
# clear_outputs()
# shift(notebook.browser, Keys.ENTER)
# outputs = notebook.wait_for_cell_output(0)
# assert outputs[0].text == '11'
# notebook.delete_cell(index=1)
#
# # Execute cell with Ctrl-Enter
# notebook.edit_cell(index=0, content='a=12; print(a)')
# clear_outputs()
# cmdtrl(notebook.browser, Keys.ENTER)
# outputs = notebook.wait_for_cell_output(0)
# assert outputs[0].text == '12'
#
# # Execute cell with toolbar button
# notebook.edit_cell(index=0, content='a=13; print(a)')
# clear_outputs()
# notebook.browser.find_element_by_css_selector(
# "button[data-jupyter-action='jupyter-notebook:run-cell-and-select-next']").click()
# outputs = notebook.wait_for_cell_output(0)
# assert outputs[0].text == '13'
#
# # Set up two cells to test stopping on error
# notebook.edit_cell(index=0, content='raise IOError')
# notebook.edit_cell(index=1, content='a=14; print(a)')
#
# # Default behaviour: stop on error
# clear_outputs()
# browser.execute_script("""
# var cell0 = Jupyter.notebook.get_cell(0);
# var cell1 = Jupyter.notebook.get_cell(1);
# cell0.execute();
# cell1.execute();
# """)
# outputs = notebook.wait_for_cell_output(0)
# assert notebook.get_cell_output(1) == []
#
# # Execute a cell with stop_on_error=false
# clear_outputs()
# browser.execute_script("""
# var cell0 = Jupyter.notebook.get_cell(0);
# var cell1 = Jupyter.notebook.get_cell(1);
# cell0.execute(false);
# cell1.execute();
# """)
# outputs = notebook.wait_for_cell_output(1)
# assert outputs[0].text == '14'
Loading