-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #170 from ericsnekbytes/playwright_conversion
Playwright Testing Conversion
- Loading branch information
Showing
64 changed files
with
2,698 additions
and
2,127 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
"""Fixtures for pytest/playwright end_to_end tests.""" | ||
|
||
|
||
import datetime | ||
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 | ||
|
||
import nbformat | ||
from nbformat.v4 import new_notebook, new_code_cell | ||
from .utils import NotebookFrontend, BROWSER_CONTEXT, BROWSER_OBJ, TREE_PAGE, SERVER_INFO | ||
|
||
|
||
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='function') | ||
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']}) | ||
|
||
|
||
@pytest.fixture(scope='function') | ||
def playwright_browser(playwright): | ||
start = datetime.datetime.now() | ||
while (datetime.datetime.now() - start).seconds < 30: | ||
try: | ||
if os.environ.get('JUPYTER_TEST_BROWSER') == 'chrome': | ||
browser = playwright.chromium.launch() | ||
else: | ||
browser = playwright.firefox.launch() | ||
break | ||
except Exception: | ||
time.sleep(.2) | ||
|
||
yield browser | ||
|
||
# Teardown | ||
browser.close() | ||
|
||
|
||
@pytest.fixture(scope='function') | ||
def authenticated_browser_data(playwright_browser, notebook_server): | ||
browser_obj = playwright_browser | ||
browser_context = browser_obj.new_context() | ||
browser_context.jupyter_server_info = notebook_server | ||
tree_page = browser_context.new_page() | ||
tree_page.goto("{url}?token={token}".format(**notebook_server)) | ||
|
||
auth_browser_data = { | ||
BROWSER_CONTEXT: browser_context, | ||
TREE_PAGE: tree_page, | ||
SERVER_INFO: notebook_server, | ||
BROWSER_OBJ: browser_obj, | ||
} | ||
|
||
return auth_browser_data | ||
|
||
|
||
@pytest.fixture(scope='function') | ||
def notebook_frontend(authenticated_browser_data): | ||
yield NotebookFrontend.new_notebook_frontend(authenticated_browser_data) | ||
|
||
|
||
@pytest.fixture(scope='function') | ||
def prefill_notebook(playwright_browser, notebook_server): | ||
browser_obj = playwright_browser | ||
browser_context = browser_obj.new_context() | ||
# playwright_browser is the browser_context, | ||
# notebook_server is the server with directories | ||
|
||
# the return of function inner takes in a dictionary of strings to populate cells | ||
def inner(cells): | ||
cells = [new_code_cell(c) if isinstance(c, str) else c | ||
for c in cells] | ||
# new_notebook is an nbformat function that is imported so that it can create a | ||
# notebook that is formatted as it needs to be | ||
nb = new_notebook(cells=cells) | ||
|
||
# Create temporary file directory and store it's reference as well as the path | ||
fd, path = mkstemp(dir=notebook_server['nbdir'], suffix='.ipynb') | ||
|
||
# Open the file and write the format onto the file | ||
with open(fd, 'w', encoding='utf-8') as f: | ||
nbformat.write(nb, f) | ||
|
||
# Grab the name of the file | ||
fname = os.path.basename(path) | ||
|
||
# Add the notebook server as a property of the playwright browser with the name jupyter_server_info | ||
browser_context.jupyter_server_info = notebook_server | ||
# Open a new page in the browser and refer to it as the tree page | ||
tree_page = browser_context.new_page() | ||
|
||
# Navigate that page to the base URL page AKA the tree page | ||
tree_page.goto("{url}?token={token}".format(**notebook_server)) | ||
|
||
auth_browser_data = { | ||
BROWSER_CONTEXT: browser_context, | ||
TREE_PAGE: tree_page, | ||
SERVER_INFO: notebook_server, | ||
BROWSER_OBJ: browser_obj | ||
} | ||
|
||
return NotebookFrontend.new_notebook_frontend(auth_browser_data, existing_file_name=fname) | ||
|
||
# Return the function that will take in the dict of code strings | ||
return inner |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
"""Test basic cell execution methods, related shortcuts, and error modes | ||
Run this manually: | ||
# Normal pytest run | ||
pytest nbclassic/tests/end_to_end/test_interrupt.py | ||
# with playwright debug (run and poke around in the web console) | ||
PWDEBUG=1 pytest -s nbclassic/tests/end_to_end/test_interrupt.py | ||
""" | ||
|
||
|
||
from .utils import TREE_PAGE, EDITOR_PAGE | ||
|
||
|
||
# # Use/uncomment this for manual test prototytping | ||
# # (the test suite will run this if it's uncommented) | ||
# def test_do_something(notebook_frontend): | ||
# # Do something with the notebook_frontend here | ||
# notebook_frontend.add_cell() | ||
# notebook_frontend.add_cell() | ||
# assert len(notebook_frontend.cells) == 3 | ||
# | ||
# notebook_frontend.delete_all_cells() | ||
# assert len(notebook_frontend.cells) == 1 | ||
# | ||
# notebook_frontend.editor_page.pause() | ||
# cell_texts = ['aa = 1', 'bb = 2', 'cc = 3'] | ||
# a, b, c = cell_texts | ||
# notebook_frontend.populate(cell_texts) | ||
# assert notebook_frontend.get_cells_contents() == [a, b, c] | ||
# notebook_frontend._pause() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
"""Tests buffering of execution requests.""" | ||
|
||
|
||
from .utils import TREE_PAGE, EDITOR_PAGE | ||
|
||
|
||
def test_kernels_buffer_without_conn(prefill_notebook): | ||
"""Test that execution request made while disconnected is buffered.""" | ||
|
||
notebook_frontend = prefill_notebook(["print(1 + 2)"]) | ||
notebook_frontend.wait_for_kernel_ready() | ||
|
||
notebook_frontend.evaluate("() => { IPython.notebook.kernel.stop_channels }", page=EDITOR_PAGE) | ||
notebook_frontend.execute_cell(0) | ||
|
||
notebook_frontend.evaluate("() => { IPython.notebook.kernel.reconnect }", page=EDITOR_PAGE) | ||
notebook_frontend.wait_for_kernel_ready() | ||
|
||
outputs = notebook_frontend.wait_for_cell_output(0) | ||
assert outputs.get_inner_text().strip() == '3' | ||
|
||
|
||
def test_buffered_cells_execute_in_order(prefill_notebook): | ||
"""Test that buffered requests execute in order.""" | ||
|
||
notebook_frontend = prefill_notebook(['', 'k=1', 'k+=1', 'k*=3', 'print(k)']) | ||
|
||
# Repeated execution of cell queued up in the kernel executes | ||
# each execution request in order. | ||
notebook_frontend.wait_for_kernel_ready() | ||
notebook_frontend.evaluate("() => IPython.notebook.kernel.stop_channels();", page=EDITOR_PAGE) | ||
# k == 1 | ||
notebook_frontend.execute_cell(1) | ||
# k == 2 | ||
notebook_frontend.execute_cell(2) | ||
# k == 6 | ||
notebook_frontend.execute_cell(3) | ||
# k == 7 | ||
notebook_frontend.execute_cell(2) | ||
notebook_frontend.execute_cell(4) | ||
notebook_frontend.evaluate("() => IPython.notebook.kernel.reconnect();", page=EDITOR_PAGE) | ||
notebook_frontend.wait_for_kernel_ready() | ||
|
||
outputs = notebook_frontend.wait_for_cell_output(4) | ||
assert outputs.get_inner_text().strip() == '7' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
"""Tests clipboard by copying, cutting and pasting multiple cells""" | ||
|
||
|
||
from .utils import TREE_PAGE, EDITOR_PAGE | ||
|
||
|
||
# Optionally perfom this test with Ctrl+c and Ctrl+v | ||
def test_clipboard_multiselect(prefill_notebook): | ||
notebook = prefill_notebook(['', '1', '2', '3', '4', '5a', '6b', '7c', '8d']) | ||
|
||
assert notebook.get_cells_contents() == ['', '1', '2', '3', '4', '5a', '6b', '7c', '8d'] | ||
|
||
# Copy the first 3 cells | ||
# Paste the values copied from the first three cells into the last 3 cells | ||
|
||
# Selecting the fist 3 cells | ||
notebook.select_cell_range(1, 3) | ||
|
||
# Copy those selected cells | ||
notebook.try_click_selector('#editlink', page=EDITOR_PAGE) | ||
notebook.try_click_selector('//*[@id="copy_cell"]/a/span[1]', page=EDITOR_PAGE) | ||
|
||
# Select the last 3 cells | ||
notebook.select_cell_range(6, 8) | ||
|
||
# Paste the cells in clipboard onto selected cells | ||
notebook.try_click_selector('#editlink', page=EDITOR_PAGE) | ||
notebook.try_click_selector('//*[@id="paste_cell_replace"]/a', page=EDITOR_PAGE) | ||
|
||
assert notebook.get_cells_contents() == ['', '1', '2', '3', '4', '5a', '1', '2', '3'] | ||
|
||
# Select the last four cells, cut them and paste them below the first cell | ||
|
||
# Select the last 4 cells | ||
notebook.select_cell_range(5, 8) | ||
|
||
# Click Edit button and the select cut button | ||
notebook.try_click_selector('#editlink', page=EDITOR_PAGE) | ||
notebook.try_click_selector('//*[@id="cut_cell"]/a', page=EDITOR_PAGE) | ||
|
||
# Select the first cell | ||
notebook.select_cell_range(0, 0) | ||
|
||
# Paste the cells in our clipboard below this first cell we are focused at | ||
notebook.try_click_selector('#editlink', page=EDITOR_PAGE) | ||
notebook.try_click_selector('//*[@id="paste_cell_below"]/a/span[1]', page=EDITOR_PAGE) | ||
|
||
assert notebook.get_cells_contents() == ['', '5a', '1', '2', '3', '1', '2', '3', '4'] |
Oops, something went wrong.