Uploaded Test files
This commit is contained in:
parent
f584ad9d97
commit
2e81cb7d99
16627 changed files with 2065359 additions and 102444 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
143
venv/Lib/site-packages/notebook/tests/selenium/conftest.py
Normal file
143
venv/Lib/site-packages/notebook/tests/selenium/conftest.py
Normal file
|
@ -0,0 +1,143 @@
|
|||
import json
|
||||
import nbformat
|
||||
from nbformat.v4 import new_notebook, new_code_cell
|
||||
import os
|
||||
import pytest
|
||||
import requests
|
||||
from subprocess import Popen
|
||||
import sys
|
||||
from tempfile import mkstemp
|
||||
from testpath.tempdir import TemporaryDirectory
|
||||
import time
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from selenium.webdriver import Firefox, Remote, Chrome
|
||||
from .utils import Notebook
|
||||
|
||||
pjoin = os.path.join
|
||||
|
||||
|
||||
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, u'sub ∂ir1', u'sub ∂ir 1a'))
|
||||
os.makedirs(pjoin(nbdir, u'sub ∂ir2', u'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', 'notebook',
|
||||
'--no-browser',
|
||||
'--notebook-dir', nbdir,
|
||||
# run with a base URL that would be escaped,
|
||||
# to test that we don't double-escape URLs
|
||||
'--NotebookApp.base_url=/a@b/',
|
||||
]
|
||||
print("command=", command)
|
||||
proc = info['popen'] = Popen(command, cwd=nbdir, env=env)
|
||||
info_file_path = pjoin(td, 'jupyter_runtime',
|
||||
'nbserver-%i.json' % proc.pid)
|
||||
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 = "%s:%s@localhost:4445" % (username, access_key)
|
||||
print("Connecting remote driver on Sauce Labs")
|
||||
driver = Remote(desired_capabilities=capabilities,
|
||||
command_executor="http://%s/wd/hub" % hub_url)
|
||||
return driver
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def selenium_driver():
|
||||
if os.environ.get('SAUCE_USERNAME'):
|
||||
driver = make_sauce_driver()
|
||||
elif os.environ.get('JUPYTER_TEST_BROWSER') == 'chrome':
|
||||
driver = Chrome()
|
||||
else:
|
||||
driver = Firefox()
|
||||
|
||||
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
|
||||
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
|
|
@ -0,0 +1,53 @@
|
|||
"""Utilities for driving Selenium interactively to develop tests.
|
||||
|
||||
These are not used in the tests themselves - rather, the developer writing tests
|
||||
can use them to experiment with Selenium.
|
||||
"""
|
||||
from selenium.webdriver import Firefox
|
||||
|
||||
from notebook.tests.selenium.utils import Notebook
|
||||
from notebook.notebookapp import list_running_servers
|
||||
|
||||
class NoServerError(Exception):
|
||||
|
||||
def __init__(self, message):
|
||||
self.message = message
|
||||
|
||||
def quick_driver(lab=False):
|
||||
"""Quickly create a selenium driver pointing at an active noteboook server.
|
||||
|
||||
Usage example:
|
||||
|
||||
from notebook.tests.selenium.quick_selenium import quick_driver
|
||||
driver = quick_driver
|
||||
|
||||
Note: you need to manually close the driver that opens with driver.quit()
|
||||
"""
|
||||
try:
|
||||
server = list(list_running_servers())[0]
|
||||
except IndexError as e:
|
||||
raise NoServerError('You need a server running before you can run '
|
||||
'this command') from e
|
||||
driver = Firefox()
|
||||
auth_url = '{url}?token={token}'.format(**server)
|
||||
driver.get(auth_url)
|
||||
|
||||
# If this redirects us to a lab page and we don't want that;
|
||||
# then we need to redirect ourselves to the classic notebook view
|
||||
if driver.current_url.endswith('/lab') and not lab:
|
||||
driver.get(driver.current_url.rstrip('lab')+'tree')
|
||||
return driver
|
||||
|
||||
|
||||
def quick_notebook():
|
||||
"""Quickly create a new classic notebook in a selenium driver
|
||||
|
||||
|
||||
Usage example:
|
||||
|
||||
from notebook.tests.selenium.quick_selenium import quick_notebook
|
||||
nb = quick_notebook()
|
||||
|
||||
Note: you need to manually close the driver that opens with nb.browser.quit()
|
||||
"""
|
||||
return Notebook.new_notebook(quick_driver())
|
|
@ -0,0 +1,50 @@
|
|||
"""Tests buffering of execution requests."""
|
||||
|
||||
from .utils import wait_for_selector
|
||||
|
||||
|
||||
def wait_for_cell_text_output(notebook, index):
|
||||
cell = notebook.cells[index]
|
||||
output = wait_for_selector(cell, ".output_text", single=True)
|
||||
return output.text
|
||||
|
||||
|
||||
def wait_for_kernel_ready(notebook):
|
||||
wait_for_selector(notebook.browser, ".kernel_idle_icon")
|
||||
|
||||
|
||||
def test_kernels_buffer_without_conn(prefill_notebook):
|
||||
"""Test that execution request made while disconnected is buffered."""
|
||||
notebook = prefill_notebook(["print(1 + 2)"])
|
||||
|
||||
wait_for_kernel_ready(notebook)
|
||||
notebook.browser.execute_script("IPython.notebook.kernel.stop_channels();")
|
||||
notebook.execute_cell(0)
|
||||
notebook.browser.execute_script("IPython.notebook.kernel.reconnect();")
|
||||
wait_for_kernel_ready(notebook)
|
||||
|
||||
assert wait_for_cell_text_output(notebook, 0) == "3"
|
||||
|
||||
|
||||
def test_buffered_cells_execute_in_order(prefill_notebook):
|
||||
"""Test that buffered requests execute in order."""
|
||||
notebook = 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.
|
||||
wait_for_kernel_ready(notebook)
|
||||
notebook.browser.execute_script("IPython.notebook.kernel.stop_channels();")
|
||||
# k == 1
|
||||
notebook.execute_cell(1)
|
||||
# k == 2
|
||||
notebook.execute_cell(2)
|
||||
# k == 6
|
||||
notebook.execute_cell(3)
|
||||
# k == 7
|
||||
notebook.execute_cell(2)
|
||||
notebook.execute_cell(4)
|
||||
notebook.browser.execute_script("IPython.notebook.kernel.reconnect();")
|
||||
wait_for_kernel_ready(notebook)
|
||||
|
||||
# Check that current value of k is 7
|
||||
assert wait_for_cell_text_output(notebook, 4) == "7"
|
|
@ -0,0 +1,27 @@
|
|||
"""Tests clipboard by copying, cutting and pasting multiple cells"""
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from .utils import wait_for_selector, wait_for_xpath
|
||||
|
||||
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']
|
||||
|
||||
# Select the first 3 cells with value and replace the last 3
|
||||
[notebook.body.send_keys(Keys.UP) for i in range(8)]
|
||||
notebook.select_cell_range(1, 3)
|
||||
notebook.body.send_keys("c")
|
||||
notebook.select_cell_range(6, 8)
|
||||
wait_for_xpath(notebook.browser, '//a[text()="Edit"]', single=True).click()
|
||||
wait_for_selector(notebook.browser, '#paste_cell_replace', single=True).click()
|
||||
|
||||
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
|
||||
notebook.select_cell_range(5, 8)
|
||||
wait_for_selector(notebook.browser, '.fa-cut.fa', single=True).click()
|
||||
for i in range(8):
|
||||
notebook.body.send_keys(Keys.UP)
|
||||
notebook.body.send_keys("v")
|
||||
|
||||
assert notebook.get_cells_contents() == ['', '5a', '1', '2', '3', '1', '2', '3', '4']
|
|
@ -0,0 +1,73 @@
|
|||
import os
|
||||
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
|
||||
from notebook.utils import url_path_join
|
||||
from notebook.tests.selenium.utils import wait_for_selector
|
||||
pjoin = os.path.join
|
||||
|
||||
|
||||
class PageError(Exception):
|
||||
"""Error for an action being incompatible with the current jupyter web page.
|
||||
|
||||
"""
|
||||
def __init__(self, message):
|
||||
self.message = message
|
||||
|
||||
|
||||
def url_in_tree(browser, url=None):
|
||||
if url is None:
|
||||
url = browser.current_url
|
||||
tree_url = url_path_join(browser.jupyter_server_info['url'], 'tree')
|
||||
return url.startswith(tree_url)
|
||||
|
||||
|
||||
def get_list_items(browser):
|
||||
"""Gets list items from a directory listing page
|
||||
|
||||
Raises PageError if not in directory listing page (url has tree in it)
|
||||
"""
|
||||
if not url_in_tree(browser):
|
||||
raise PageError("You are not in the notebook's file tree view."
|
||||
"This function can only be used the file tree context.")
|
||||
# we need to make sure that at least one item link loads
|
||||
wait_for_selector(browser, '.item_link')
|
||||
|
||||
return [{
|
||||
'link': a.get_attribute('href'),
|
||||
'label': a.find_element_by_class_name('item_name').text,
|
||||
'element': a,
|
||||
} for a in browser.find_elements_by_class_name('item_link')]
|
||||
|
||||
def only_dir_links(browser):
|
||||
"""Return only links that point at other directories in the tree
|
||||
|
||||
"""
|
||||
items = get_list_items(browser)
|
||||
return [i for i in items
|
||||
if url_in_tree(browser, i['link']) and i['label'] != '..']
|
||||
|
||||
def test_items(authenticated_browser):
|
||||
visited_dict = {}
|
||||
# Going down the tree to collect links
|
||||
while True:
|
||||
wait_for_selector(authenticated_browser, '.item_link')
|
||||
current_url = authenticated_browser.current_url
|
||||
items = visited_dict[current_url] = only_dir_links(authenticated_browser)
|
||||
try:
|
||||
item = items[0]
|
||||
item["element"].click()
|
||||
assert authenticated_browser.current_url == item['link']
|
||||
except IndexError:
|
||||
break
|
||||
# Going back up the tree while we still have unvisited links
|
||||
while visited_dict:
|
||||
current_items = only_dir_links(authenticated_browser)
|
||||
current_items_links = [item["link"] for item in current_items]
|
||||
stored_items = visited_dict.pop(authenticated_browser.current_url)
|
||||
stored_items_links = [item["link"] for item in stored_items]
|
||||
assert stored_items_links == current_items_links
|
||||
authenticated_browser.back()
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
|
||||
def cell_is_deletable(nb, index):
|
||||
JS = 'return Jupyter.notebook.get_cell({}).is_deletable();'.format(index)
|
||||
return nb.browser.execute_script(JS)
|
||||
|
||||
def remove_all_cells(notebook):
|
||||
for i in range(len(notebook.cells)):
|
||||
notebook.delete_cell(0)
|
||||
|
||||
INITIAL_CELLS = ['print("a")', 'print("b")', 'print("c")']
|
||||
|
||||
def test_delete_cells(prefill_notebook):
|
||||
a, b, c = INITIAL_CELLS
|
||||
notebook = prefill_notebook(INITIAL_CELLS)
|
||||
|
||||
# Validate initial state
|
||||
assert notebook.get_cells_contents() == [a, b, c]
|
||||
for cell in range(0, 3):
|
||||
assert cell_is_deletable(notebook, cell)
|
||||
|
||||
notebook.set_cell_metadata(0, 'deletable', 'false')
|
||||
notebook.set_cell_metadata(1, 'deletable', 0
|
||||
)
|
||||
assert not cell_is_deletable(notebook, 0)
|
||||
assert cell_is_deletable(notebook, 1)
|
||||
assert cell_is_deletable(notebook, 2)
|
||||
|
||||
# Try to delete cell a (should not be deleted)
|
||||
notebook.delete_cell(0)
|
||||
assert notebook.get_cells_contents() == [a, b, c]
|
||||
|
||||
# Try to delete cell b (should succeed)
|
||||
notebook.delete_cell(1)
|
||||
assert notebook.get_cells_contents() == [a, c]
|
||||
|
||||
# Try to delete cell c (should succeed)
|
||||
notebook.delete_cell(1)
|
||||
assert notebook.get_cells_contents() == [a]
|
||||
|
||||
# Change the deletable state of cell a
|
||||
notebook.set_cell_metadata(0, 'deletable', 'true')
|
||||
|
||||
# Try to delete cell a (should succeed)
|
||||
notebook.delete_cell(0)
|
||||
assert len(notebook.cells) == 1 # it contains an empty cell
|
||||
|
||||
# Make sure copied cells are deletable
|
||||
notebook.edit_cell(index=0, content=a)
|
||||
notebook.set_cell_metadata(0, 'deletable', 'false')
|
||||
assert not cell_is_deletable(notebook, 0)
|
||||
notebook.to_command_mode()
|
||||
notebook.current_cell.send_keys('cv')
|
||||
assert len(notebook.cells) == 2
|
||||
assert cell_is_deletable(notebook, 1)
|
||||
|
||||
notebook.set_cell_metadata(0, 'deletable', 'true') # to perform below test, remove all the cells
|
||||
remove_all_cells(notebook)
|
||||
assert len(notebook.cells) == 1 # notebook should create one automatically on empty notebook
|
|
@ -0,0 +1,65 @@
|
|||
"""Test display of images
|
||||
|
||||
The effect of shape metadata is validated, using Image(retina=True)
|
||||
"""
|
||||
|
||||
from .utils import wait_for_tag
|
||||
|
||||
|
||||
# 2x2 black square in b64 jpeg and png
|
||||
b64_image_data = {
|
||||
"image/png" : b'iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAC0lEQVR4nGNgQAYAAA4AAamRc7EA\\nAAAASUVORK5CYII',
|
||||
"image/jpeg" : b'/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0a\nHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIy\nMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAACAAIDASIA\nAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQA\nAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3\nODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWm\np6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEA\nAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSEx\nBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElK\nU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3\nuLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD5/ooo\noAoo2Qoo'
|
||||
}
|
||||
|
||||
|
||||
def imports(notebook):
|
||||
commands = [
|
||||
'import base64',
|
||||
'from IPython.display import display, Image',
|
||||
]
|
||||
notebook.edit_cell(index=0, content="\n".join(commands))
|
||||
notebook.execute_cell(0)
|
||||
|
||||
|
||||
def validate_img(notebook, cell_index, image_fmt, retina):
|
||||
"""Validate that image renders as expected."""
|
||||
|
||||
b64data = b64_image_data[image_fmt]
|
||||
commands = [
|
||||
'b64data = %s' % b64data,
|
||||
'data = base64.decodebytes(b64data)',
|
||||
'display(Image(data, retina=%s))' % retina
|
||||
]
|
||||
notebook.append("\n".join(commands))
|
||||
notebook.execute_cell(cell_index)
|
||||
|
||||
# Find the image element that was just displayed
|
||||
wait_for_tag(notebook.cells[cell_index], "img", single=True)
|
||||
img_element = notebook.cells[cell_index].find_element_by_tag_name("img")
|
||||
|
||||
src = img_element.get_attribute("src")
|
||||
prefix = src.split(',')[0]
|
||||
expected_prefix = "data:%s;base64" % image_fmt
|
||||
assert prefix == expected_prefix
|
||||
|
||||
expected_size = 1 if retina else 2
|
||||
assert img_element.size["width"] == expected_size
|
||||
assert img_element.size["height"] == expected_size
|
||||
assert img_element.get_attribute("width") == str(expected_size)
|
||||
assert img_element.get_attribute("height") == str(expected_size)
|
||||
|
||||
|
||||
def test_display_image(notebook):
|
||||
imports(notebook)
|
||||
# PNG, non-retina
|
||||
validate_img(notebook, 1, "image/png", False)
|
||||
|
||||
# PNG, retina display
|
||||
validate_img(notebook, 2, "image/png", True)
|
||||
|
||||
# JPEG, non-retina
|
||||
validate_img(notebook, 3, "image/jpeg", False)
|
||||
|
||||
# JPEG, retina display
|
||||
validate_img(notebook, 4, "image/jpeg", True)
|
|
@ -0,0 +1,96 @@
|
|||
"""Test display isolation.
|
||||
|
||||
An object whose metadata contains an "isolated" tag must be isolated
|
||||
from the rest of the document.
|
||||
"""
|
||||
from .utils import wait_for_tag
|
||||
|
||||
|
||||
def test_display_isolation(notebook):
|
||||
import_ln = "from IPython.core.display import HTML, SVG, display, display_svg"
|
||||
notebook.edit_cell(index=0, content=import_ln)
|
||||
notebook.execute_cell(notebook.current_cell)
|
||||
try:
|
||||
isolated_html(notebook)
|
||||
isolated_svg(notebook)
|
||||
finally:
|
||||
# Ensure we switch from iframe back to default content even if test fails
|
||||
notebook.browser.switch_to.default_content()
|
||||
|
||||
|
||||
def isolated_html(notebook):
|
||||
"""Test HTML display isolation.
|
||||
|
||||
HTML styling rendered without isolation will affect the whole
|
||||
document, whereas styling applied with isolation will affect only
|
||||
the local display object.
|
||||
"""
|
||||
red = 'rgb(255, 0, 0)'
|
||||
blue = 'rgb(0, 0, 255)'
|
||||
test_str = "<div id='test'>Should turn red from non-isolation</div>"
|
||||
notebook.add_and_execute_cell(content="display(HTML(%r))" % test_str)
|
||||
non_isolated = (
|
||||
"<style>div{color:%s;}</style>" % red +
|
||||
"<div id='non-isolated'>Should be red</div>")
|
||||
display_ni = "display(HTML(%r), metadata={'isolated':False})" % (
|
||||
non_isolated)
|
||||
notebook.add_and_execute_cell(content=display_ni)
|
||||
isolated = (
|
||||
"<style>div{color:%s;}</style>" % blue +
|
||||
"<div id='isolated'>Should be blue</div>")
|
||||
display_i = "display(HTML(%r), metadata={'isolated':True})" % (
|
||||
isolated)
|
||||
notebook.add_and_execute_cell(content=display_i)
|
||||
|
||||
iframe = wait_for_tag(notebook.browser, "iframe", single=True)
|
||||
|
||||
# The non-isolated div will be in the body
|
||||
non_isolated_div = notebook.body.find_element_by_id("non-isolated")
|
||||
assert non_isolated_div.value_of_css_property("color") == red
|
||||
|
||||
# The non-isolated styling will have affected the output of other cells
|
||||
test_div = notebook.body.find_element_by_id("test")
|
||||
assert test_div.value_of_css_property("color") == red
|
||||
|
||||
# The isolated div will be in an iframe, only that element will be blue
|
||||
notebook.browser.switch_to.frame(iframe)
|
||||
isolated_div = notebook.browser.find_element_by_id("isolated")
|
||||
assert isolated_div.value_of_css_property("color") == blue
|
||||
notebook.browser.switch_to.default_content()
|
||||
# Clean up the html test cells
|
||||
for i in range(1, len(notebook.cells)):
|
||||
notebook.delete_cell(1)
|
||||
|
||||
|
||||
def isolated_svg(notebook):
|
||||
"""Test that multiple isolated SVGs have different scopes.
|
||||
|
||||
Asserts that there no CSS leaks between two isolated SVGs.
|
||||
"""
|
||||
yellow = "rgb(255, 255, 0)"
|
||||
black = "rgb(0, 0, 0)"
|
||||
svg_1_str = """s1 = '''<svg width="1cm" height="1cm" viewBox="0 0 1000 500"><defs><style>rect {fill:%s;}; </style></defs><rect id="r1" x="200" y="100" width="600" height="300" /></svg>'''""" % yellow
|
||||
svg_2_str = """s2 = '''<svg width="1cm" height="1cm" viewBox="0 0 1000 500"><rect id="r2" x="200" y="100" width="600" height="300" /></svg>'''"""
|
||||
|
||||
notebook.add_and_execute_cell(content=svg_1_str)
|
||||
notebook.add_and_execute_cell(content=svg_2_str)
|
||||
notebook.add_and_execute_cell(
|
||||
content="display_svg(SVG(s1), metadata=dict(isolated=True))")
|
||||
notebook.add_and_execute_cell(
|
||||
content="display_svg(SVG(s2), metadata=dict(isolated=True))")
|
||||
iframes = wait_for_tag(notebook.browser, "iframe", wait_for_n=2)
|
||||
|
||||
# The first rectangle will be red
|
||||
notebook.browser.switch_to.frame(iframes[0])
|
||||
isolated_svg_1 = notebook.browser.find_element_by_id('r1')
|
||||
assert isolated_svg_1.value_of_css_property("fill") == yellow
|
||||
notebook.browser.switch_to.default_content()
|
||||
|
||||
# The second rectangle will be black
|
||||
notebook.browser.switch_to.frame(iframes[1])
|
||||
isolated_svg_2 = notebook.browser.find_element_by_id('r2')
|
||||
assert isolated_svg_2.value_of_css_property("fill") == black
|
||||
|
||||
# Clean up the svg test cells
|
||||
for i in range(1, len(notebook.cells)):
|
||||
notebook.delete_cell(1)
|
|
@ -0,0 +1,103 @@
|
|||
"""Tests arrow keys on both command and edit mode"""
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
|
||||
def test_dualmode_arrows(notebook):
|
||||
|
||||
# Tests in command mode.
|
||||
# Setting up the cells to test the keys to move up.
|
||||
notebook.to_command_mode()
|
||||
[notebook.body.send_keys("b") for i in range(3)]
|
||||
|
||||
# Use both "k" and up arrow keys to moving up and enter a value.
|
||||
# Once located on the top cell, use the up arrow keys to prove the top cell is still selected.
|
||||
notebook.body.send_keys("k")
|
||||
notebook.body.send_keys(Keys.ENTER)
|
||||
notebook.body.send_keys("2")
|
||||
notebook.to_command_mode()
|
||||
notebook.body.send_keys(Keys.UP)
|
||||
notebook.body.send_keys(Keys.ENTER)
|
||||
notebook.body.send_keys("1")
|
||||
notebook.to_command_mode()
|
||||
notebook.body.send_keys("k")
|
||||
notebook.body.send_keys(Keys.UP)
|
||||
notebook.body.send_keys(Keys.ENTER)
|
||||
notebook.body.send_keys("0")
|
||||
notebook.to_command_mode()
|
||||
assert notebook.get_cells_contents() == ["0", "1", "2", ""]
|
||||
|
||||
# Use the "k" key on the top cell as well
|
||||
notebook.body.send_keys("k")
|
||||
notebook.body.send_keys(Keys.ENTER)
|
||||
notebook.body.send_keys(" edit #1")
|
||||
notebook.to_command_mode()
|
||||
assert notebook.get_cells_contents() == ["0 edit #1", "1", "2", ""]
|
||||
|
||||
# Setting up the cells to test the keys to move down
|
||||
[notebook.body.send_keys("j") for i in range(3)]
|
||||
[notebook.body.send_keys("a") for i in range(2)]
|
||||
notebook.body.send_keys("k")
|
||||
|
||||
# Use both "j" key and down arrow keys to moving down and enter a value.
|
||||
# Once located on the bottom cell, use the down arrow key to prove the bottom cell is still selected.
|
||||
notebook.body.send_keys(Keys.DOWN)
|
||||
notebook.body.send_keys(Keys.ENTER)
|
||||
notebook.body.send_keys("3")
|
||||
notebook.to_command_mode()
|
||||
notebook.body.send_keys("j")
|
||||
notebook.body.send_keys(Keys.ENTER)
|
||||
notebook.body.send_keys("4")
|
||||
notebook.to_command_mode()
|
||||
notebook.body.send_keys("j")
|
||||
notebook.body.send_keys(Keys.DOWN)
|
||||
notebook.body.send_keys(Keys.ENTER)
|
||||
notebook.body.send_keys("5")
|
||||
notebook.to_command_mode()
|
||||
assert notebook.get_cells_contents() == ["0 edit #1", "1", "2", "3", "4", "5"]
|
||||
|
||||
# Use the "j" key on the top cell as well
|
||||
notebook.body.send_keys("j")
|
||||
notebook.body.send_keys(Keys.ENTER)
|
||||
notebook.body.send_keys(" edit #1")
|
||||
notebook.to_command_mode()
|
||||
assert notebook.get_cells_contents() == ["0 edit #1", "1", "2", "3", "4", "5 edit #1"]
|
||||
|
||||
# On the bottom cell, use both left and right arrow keys to prove the bottom cell is still selected.
|
||||
notebook.body.send_keys(Keys.LEFT)
|
||||
notebook.body.send_keys(Keys.ENTER)
|
||||
notebook.body.send_keys(", #2")
|
||||
notebook.to_command_mode()
|
||||
assert notebook.get_cells_contents() == ["0 edit #1", "1", "2", "3", "4", "5 edit #1, #2"]
|
||||
notebook.body.send_keys(Keys.RIGHT)
|
||||
notebook.body.send_keys(Keys.ENTER)
|
||||
notebook.body.send_keys(" and #3")
|
||||
notebook.to_command_mode()
|
||||
assert notebook.get_cells_contents() == ["0 edit #1", "1", "2", "3", "4", "5 edit #1, #2 and #3"]
|
||||
|
||||
|
||||
# Tests in edit mode.
|
||||
# First, erase the previous content and then setup the cells to test the keys to move up.
|
||||
[notebook.browser.find_element_by_class_name("fa-cut.fa").click() for i in range(6)]
|
||||
[notebook.body.send_keys("b") for i in range(2)]
|
||||
notebook.body.send_keys("a")
|
||||
notebook.body.send_keys(Keys.ENTER)
|
||||
|
||||
# Use the up arrow key to move down and enter a value.
|
||||
# We will use the left arrow key to move one char to the left since moving up on last character only moves selector to the first one.
|
||||
# Once located on the top cell, use the up arrow key to prove the top cell is still selected.
|
||||
notebook.body.send_keys(Keys.UP)
|
||||
notebook.body.send_keys("1")
|
||||
notebook.body.send_keys(Keys.LEFT)
|
||||
[notebook.body.send_keys(Keys.UP) for i in range(2)]
|
||||
notebook.body.send_keys("0")
|
||||
|
||||
# Use the down arrow key to move down and enter a value.
|
||||
# We will use the right arrow key to move one char to the right since moving down puts selector to the last character.
|
||||
# Once located on the bottom cell, use the down arrow key to prove the bottom cell is still selected.
|
||||
notebook.body.send_keys(Keys.DOWN)
|
||||
notebook.body.send_keys(Keys.RIGHT)
|
||||
notebook.body.send_keys(Keys.DOWN)
|
||||
notebook.body.send_keys("2")
|
||||
[notebook.body.send_keys(Keys.DOWN) for i in range(2)]
|
||||
notebook.body.send_keys("3")
|
||||
notebook.to_command_mode()
|
||||
assert notebook.get_cells_contents() == ["0", "1", "2", "3"]
|
|
@ -0,0 +1,58 @@
|
|||
"""Test keyboard shortcuts that change the cell's mode."""
|
||||
|
||||
def test_dualmode_cellmode(notebook):
|
||||
def get_cell_cm_mode(index):
|
||||
code_mirror_mode = notebook.browser.execute_script(
|
||||
"return Jupyter.notebook.get_cell(%s).code_mirror.getMode().name;"%index)
|
||||
return code_mirror_mode
|
||||
|
||||
|
||||
index = 0
|
||||
a = 'hello\nmulti\nline'
|
||||
|
||||
notebook.edit_cell(index=index, content=a)
|
||||
|
||||
"""check for the default cell type"""
|
||||
notebook.to_command_mode()
|
||||
notebook.body.send_keys("r")
|
||||
assert notebook.get_cell_type(index) == 'raw'
|
||||
assert get_cell_cm_mode(index) == 'null'
|
||||
|
||||
"""check cell type after changing to markdown"""
|
||||
notebook.body.send_keys("1")
|
||||
assert notebook.get_cell_type(index) == 'markdown'
|
||||
assert notebook.get_cell_contents(index) == '# ' + a
|
||||
assert get_cell_cm_mode(index) == 'ipythongfm'
|
||||
|
||||
notebook.body.send_keys("2")
|
||||
assert notebook.get_cell_type(index) == 'markdown'
|
||||
assert notebook.get_cell_contents(index) == '## ' + a
|
||||
|
||||
notebook.body.send_keys("3")
|
||||
assert notebook.get_cell_type(index) == 'markdown'
|
||||
assert notebook.get_cell_contents(index) == '### ' + a
|
||||
|
||||
notebook.body.send_keys("4")
|
||||
assert notebook.get_cell_type(index) == 'markdown'
|
||||
assert notebook.get_cell_contents(index) == '#### ' + a
|
||||
|
||||
notebook.body.send_keys("5")
|
||||
assert notebook.get_cell_type(index) == 'markdown'
|
||||
assert notebook.get_cell_contents(index) == '##### ' + a
|
||||
|
||||
notebook.body.send_keys("6")
|
||||
assert notebook.get_cell_type(index) == 'markdown'
|
||||
assert notebook.get_cell_contents(index) == '###### ' + a
|
||||
|
||||
notebook.body.send_keys("m")
|
||||
assert notebook.get_cell_type(index) == 'markdown'
|
||||
assert notebook.get_cell_contents(index) == '###### ' + a
|
||||
|
||||
notebook.body.send_keys("y")
|
||||
assert notebook.get_cell_type(index) == 'code'
|
||||
assert notebook.get_cell_contents(index) == '###### ' + a
|
||||
assert get_cell_cm_mode(index) == 'ipython'
|
||||
|
||||
notebook.body.send_keys("1")
|
||||
assert notebook.get_cell_type(index) == 'markdown'
|
||||
assert notebook.get_cell_contents(index) == '# ' + a
|
|
@ -0,0 +1,54 @@
|
|||
"""Test"""
|
||||
from .utils import shift, validate_dualmode_state
|
||||
|
||||
INITIAL_CELLS = ['', 'print("a")', 'print("b")', 'print("c")']
|
||||
|
||||
def test_dualmode_clipboard(prefill_notebook):
|
||||
notebook = prefill_notebook(INITIAL_CELLS)
|
||||
_, a, b, c = INITIAL_CELLS
|
||||
for i in range(1, 4):
|
||||
notebook.execute_cell(i)
|
||||
|
||||
#Copy/past/cut
|
||||
num_cells = len(notebook.cells)
|
||||
assert notebook.get_cell_contents(1) == a #Cell 1 is a
|
||||
|
||||
notebook.focus_cell(1)
|
||||
notebook.body.send_keys("x") #Cut
|
||||
validate_dualmode_state(notebook, 'command', 1)
|
||||
assert notebook.get_cell_contents(1) == b #Cell 2 is now where cell 1 was
|
||||
assert len(notebook.cells) == num_cells-1 #A cell was removed
|
||||
|
||||
notebook.focus_cell(2)
|
||||
notebook.body.send_keys("v") #Paste
|
||||
validate_dualmode_state(notebook, 'command', 3)
|
||||
assert notebook.get_cell_contents(3) == a #Cell 3 has the cut contents
|
||||
assert len(notebook.cells) == num_cells #A cell was added
|
||||
|
||||
notebook.body.send_keys("v") #Paste
|
||||
validate_dualmode_state(notebook, 'command', 4)
|
||||
assert notebook.get_cell_contents(4) == a #Cell a has the cut contents
|
||||
assert len(notebook.cells) == num_cells+1 #A cell was added
|
||||
|
||||
notebook.focus_cell(1)
|
||||
notebook.body.send_keys("c") #Copy
|
||||
validate_dualmode_state(notebook, 'command', 1)
|
||||
assert notebook.get_cell_contents(1) == b #Cell 1 is b
|
||||
|
||||
notebook.focus_cell(2)
|
||||
notebook.body.send_keys("c") #Copy
|
||||
validate_dualmode_state(notebook, 'command', 2)
|
||||
assert notebook.get_cell_contents(2) == c #Cell 2 is c
|
||||
|
||||
notebook.focus_cell(4)
|
||||
notebook.body.send_keys("v") #Paste
|
||||
validate_dualmode_state(notebook, 'command', 5)
|
||||
assert notebook.get_cell_contents(2) == c #Cell 2 has the copied contents
|
||||
assert notebook.get_cell_contents(5) == c #Cell 5 has the copied contents
|
||||
assert len(notebook.cells) == num_cells+2 #A cell was added
|
||||
|
||||
notebook.focus_cell(0)
|
||||
shift(notebook.browser, 'v') #Paste
|
||||
validate_dualmode_state(notebook, 'command', 0)
|
||||
assert notebook.get_cell_contents(0) == c #Cell 0 has the copied contents
|
||||
assert len(notebook.cells) == num_cells+3 #A cell was added
|
|
@ -0,0 +1,74 @@
|
|||
''' Test keyboard invoked execution '''
|
||||
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
|
||||
from .utils import shift, cmdtrl, alt, validate_dualmode_state
|
||||
|
||||
INITIAL_CELLS = ['', 'print("a")', 'print("b")', 'print("c")']
|
||||
|
||||
def test_dualmode_execute(prefill_notebook):
|
||||
notebook = prefill_notebook(INITIAL_CELLS)
|
||||
for i in range(1, 4):
|
||||
notebook.execute_cell(i)
|
||||
|
||||
#shift-enter
|
||||
#last cell in notebook
|
||||
base_index = 3
|
||||
notebook.focus_cell(base_index)
|
||||
shift(notebook.browser, Keys.ENTER) #creates one cell
|
||||
validate_dualmode_state(notebook, 'edit', base_index + 1)
|
||||
|
||||
#Not last cell in notebook & starts in edit mode
|
||||
notebook.focus_cell(base_index)
|
||||
notebook.body.send_keys(Keys.ENTER) #Enter edit mode
|
||||
validate_dualmode_state(notebook, 'edit', base_index)
|
||||
shift(notebook.browser, Keys.ENTER) #creates one cell
|
||||
validate_dualmode_state(notebook, 'command', base_index + 1)
|
||||
|
||||
#Starts in command mode
|
||||
notebook.body.send_keys('k')
|
||||
validate_dualmode_state(notebook, 'command', base_index)
|
||||
shift(notebook.browser, Keys.ENTER) #creates one cell
|
||||
validate_dualmode_state(notebook, 'command', base_index + 1)
|
||||
|
||||
|
||||
#Ctrl-enter
|
||||
#Last cell in notebook
|
||||
base_index += 1
|
||||
cmdtrl(notebook.browser, Keys.ENTER)
|
||||
validate_dualmode_state(notebook, 'command', base_index)
|
||||
|
||||
#Not last cell in notebook & stats in edit mode
|
||||
notebook.focus_cell(base_index - 1)
|
||||
notebook.body.send_keys(Keys.ENTER) #Enter edit mode
|
||||
validate_dualmode_state(notebook, 'edit', base_index - 1)
|
||||
cmdtrl(notebook.browser, Keys.ENTER)
|
||||
|
||||
#Starts in command mode
|
||||
notebook.body.send_keys('j')
|
||||
validate_dualmode_state(notebook, 'command', base_index)
|
||||
cmdtrl(notebook.browser, Keys.ENTER)
|
||||
validate_dualmode_state(notebook, 'command', base_index)
|
||||
|
||||
|
||||
#Alt-enter
|
||||
#Last cell in notebook
|
||||
alt(notebook.browser, Keys.ENTER)
|
||||
validate_dualmode_state(notebook, 'edit', base_index + 1)
|
||||
#Not last cell in notebook &starts in edit mode
|
||||
notebook.focus_cell(base_index)
|
||||
notebook.body.send_keys(Keys.ENTER) #Enter edit mode
|
||||
validate_dualmode_state(notebook, 'edit', base_index)
|
||||
alt(notebook.browser, Keys.ENTER)
|
||||
validate_dualmode_state(notebook, 'edit', base_index + 1)
|
||||
#starts in command mode
|
||||
notebook.body.send_keys(Keys.ESCAPE, 'k')
|
||||
validate_dualmode_state(notebook, 'command', base_index)
|
||||
alt(notebook.browser, Keys.ENTER)
|
||||
validate_dualmode_state(notebook, 'edit', base_index + 1)
|
||||
|
||||
|
||||
#Notebook will now have 8 cells, the index of the last cell will be 7
|
||||
assert len(notebook) == 8 #Cells where added
|
||||
notebook.focus_cell(7)
|
||||
validate_dualmode_state(notebook, 'command', 7)
|
|
@ -0,0 +1,51 @@
|
|||
from selenium.webdriver.common.keys import Keys
|
||||
from .utils import shift
|
||||
|
||||
INITIAL_CELLS = ['print("a")', 'print("b")', 'print("c")']
|
||||
|
||||
def test_insert_cell(prefill_notebook):
|
||||
notebook = prefill_notebook(INITIAL_CELLS)
|
||||
|
||||
notebook.to_command_mode()
|
||||
notebook.focus_cell(2)
|
||||
notebook.convert_cell_type(2, "markdown")
|
||||
|
||||
# insert code cell above
|
||||
notebook.current_cell.send_keys("a")
|
||||
assert notebook.get_cell_contents(2) == ""
|
||||
assert notebook.get_cell_type(2) == "code"
|
||||
assert len(notebook.cells) == 4
|
||||
|
||||
# insert code cell below
|
||||
notebook.current_cell.send_keys("b")
|
||||
assert notebook.get_cell_contents(2) == ""
|
||||
assert notebook.get_cell_contents(3) == ""
|
||||
assert notebook.get_cell_type(3) == "code"
|
||||
assert len(notebook.cells) == 5
|
||||
|
||||
notebook.edit_cell(index=1, content="cell1")
|
||||
notebook.focus_cell(1)
|
||||
notebook.current_cell.send_keys("a")
|
||||
assert notebook.get_cell_contents(1) == ""
|
||||
assert notebook.get_cell_contents(2) == "cell1"
|
||||
|
||||
notebook.edit_cell(index=1, content='cell1')
|
||||
notebook.edit_cell(index=2, content='cell2')
|
||||
notebook.edit_cell(index=3, content='cell3')
|
||||
notebook.focus_cell(2)
|
||||
notebook.current_cell.send_keys("b")
|
||||
assert notebook.get_cell_contents(1) == "cell1"
|
||||
assert notebook.get_cell_contents(2) == "cell2"
|
||||
assert notebook.get_cell_contents(3) == ""
|
||||
assert notebook.get_cell_contents(4) == "cell3"
|
||||
|
||||
# insert above multiple selected cells
|
||||
notebook.focus_cell(1)
|
||||
shift(notebook.browser, Keys.DOWN)
|
||||
notebook.current_cell.send_keys('a')
|
||||
|
||||
# insert below multiple selected cells
|
||||
notebook.focus_cell(2)
|
||||
shift(notebook.browser, Keys.DOWN)
|
||||
notebook.current_cell.send_keys('b')
|
||||
assert notebook.get_cells_contents()[1:5] == ["", "cell1", "cell2", ""]
|
|
@ -0,0 +1,53 @@
|
|||
'''Test'''
|
||||
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
|
||||
from .utils import cmdtrl, shift, validate_dualmode_state
|
||||
|
||||
def test_dualmode_markdown(notebook):
|
||||
def is_cell_rendered(index):
|
||||
JS = 'return !!IPython.notebook.get_cell(%s).rendered;'%index
|
||||
return notebook.browser.execute_script(JS)
|
||||
|
||||
|
||||
a = 'print("a")'
|
||||
index = 1
|
||||
notebook.append(a)
|
||||
|
||||
#Markdown rendering / unrendering
|
||||
notebook.focus_cell(index)
|
||||
validate_dualmode_state(notebook, 'command', index)
|
||||
notebook.body.send_keys("m")
|
||||
assert notebook.get_cell_type(index) == 'markdown'
|
||||
assert not is_cell_rendered(index) #cell is not rendered
|
||||
|
||||
notebook.body.send_keys(Keys.ENTER)#cell is unrendered
|
||||
assert not is_cell_rendered(index) #cell is not rendered
|
||||
validate_dualmode_state(notebook, 'edit', index)
|
||||
|
||||
cmdtrl(notebook.browser, Keys.ENTER)
|
||||
assert is_cell_rendered(index) #cell is rendered with crtl+enter
|
||||
validate_dualmode_state(notebook, 'command', index)
|
||||
|
||||
notebook.body.send_keys(Keys.ENTER)#cell is unrendered
|
||||
assert not is_cell_rendered(index) #cell is not rendered
|
||||
|
||||
notebook.focus_cell(index - 1)
|
||||
assert not is_cell_rendered(index) #Select index-1; cell index is still not rendered
|
||||
validate_dualmode_state(notebook, 'command', index - 1)
|
||||
|
||||
notebook.focus_cell(index)
|
||||
validate_dualmode_state(notebook, 'command', index)
|
||||
cmdtrl(notebook.browser, Keys.ENTER)
|
||||
assert is_cell_rendered(index)#Cell is rendered
|
||||
|
||||
notebook.focus_cell(index - 1)
|
||||
validate_dualmode_state(notebook, 'command', index - 1)
|
||||
|
||||
shift(notebook.browser, Keys.ENTER)
|
||||
validate_dualmode_state(notebook, 'command', index)
|
||||
assert is_cell_rendered(index)#Cell is rendered
|
||||
|
||||
shift(notebook.browser, Keys.ENTER)
|
||||
validate_dualmode_state(notebook, 'edit', index + 1)
|
||||
assert is_cell_rendered(index)#Cell is rendered
|
|
@ -0,0 +1,66 @@
|
|||
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.browser.execute_script(
|
||||
"Jupyter.notebook.clear_all_output();")
|
||||
|
||||
# Execute cell with Javascript API
|
||||
notebook.edit_cell(index=0, content='a=10; print(a)')
|
||||
browser.execute_script("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'
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
INITIAL_CELLS = ["hello", "hellohello", "abc", "ello"]
|
||||
|
||||
def test_find_and_replace(prefill_notebook):
|
||||
""" test find and replace on all the cells """
|
||||
notebook = prefill_notebook(INITIAL_CELLS)
|
||||
|
||||
find_str = "ello"
|
||||
replace_str = "foo"
|
||||
|
||||
# replace the strings
|
||||
notebook.find_and_replace(index=0, find_txt=find_str, replace_txt=replace_str)
|
||||
|
||||
# check content of the cells
|
||||
assert notebook.get_cells_contents() == [
|
||||
s.replace(find_str, replace_str) for s in INITIAL_CELLS
|
||||
]
|
|
@ -0,0 +1,36 @@
|
|||
from .utils import wait_for_selector
|
||||
|
||||
def interrupt_from_menu(notebook):
|
||||
# Click interrupt button in kernel menu
|
||||
notebook.browser.find_element_by_id('kernellink').click()
|
||||
wait_for_selector(notebook.browser, '#int_kernel', single=True).click()
|
||||
|
||||
def interrupt_from_keyboard(notebook):
|
||||
notebook.body.send_keys("ii")
|
||||
|
||||
|
||||
def test_interrupt(notebook):
|
||||
""" Test the interrupt function using both the button in the Kernel menu and the keyboard shortcut "ii"
|
||||
|
||||
Having trouble accessing the Interrupt message when execution is halted. I am assuming that the
|
||||
message does not lie in the "outputs" field of the cell's JSON object. Using a timeout work-around for
|
||||
test with an infinite loop. We know the interrupt function is working if this test passes.
|
||||
Hope this is a good start.
|
||||
"""
|
||||
|
||||
text = ('import time\n'
|
||||
'for x in range(3):\n'
|
||||
' time.sleep(1)')
|
||||
|
||||
notebook.edit_cell(index=0, content=text)
|
||||
|
||||
for interrupt_method in (interrupt_from_menu, interrupt_from_keyboard):
|
||||
notebook.clear_cell_output(0)
|
||||
notebook.to_command_mode()
|
||||
notebook.execute_cell(0)
|
||||
|
||||
interrupt_method(notebook)
|
||||
|
||||
# Wait for an output to appear
|
||||
output = wait_for_selector(notebook.browser, '.output_subarea', single=True)
|
||||
assert 'KeyboardInterrupt' in output.text
|
|
@ -0,0 +1,60 @@
|
|||
from selenium.common.exceptions import TimeoutException
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from notebook.tests.selenium.utils import wait_for_selector
|
||||
|
||||
restart_selectors = [
|
||||
'#restart_kernel', '#restart_clear_output', '#restart_run_all'
|
||||
]
|
||||
notify_interaction = '#notification_kernel > span'
|
||||
|
||||
shutdown_selector = '#shutdown_kernel'
|
||||
confirm_selector = '.btn-danger'
|
||||
cancel_selector = ".modal-footer button:first-of-type"
|
||||
|
||||
|
||||
def test_cancel_restart_or_shutdown(notebook):
|
||||
"""Click each of the restart options, then cancel the confirmation dialog"""
|
||||
browser = notebook.browser
|
||||
kernel_menu = browser.find_element_by_id('kernellink')
|
||||
|
||||
for menu_item in restart_selectors + [shutdown_selector]:
|
||||
kernel_menu.click()
|
||||
wait_for_selector(browser, menu_item, visible=True, single=True).click()
|
||||
wait_for_selector(browser, cancel_selector, visible=True, single=True).click()
|
||||
WebDriverWait(browser, 3).until(
|
||||
EC.invisibility_of_element((By.CSS_SELECTOR, '.modal-backdrop'))
|
||||
)
|
||||
assert notebook.is_kernel_running()
|
||||
|
||||
|
||||
def test_menu_items(notebook):
|
||||
browser = notebook.browser
|
||||
kernel_menu = browser.find_element_by_id('kernellink')
|
||||
|
||||
for menu_item in restart_selectors:
|
||||
# Shutdown
|
||||
kernel_menu.click()
|
||||
wait_for_selector(browser, shutdown_selector, visible=True, single=True).click()
|
||||
|
||||
# Confirm shutdown
|
||||
wait_for_selector(browser, confirm_selector, visible=True, single=True).click()
|
||||
|
||||
WebDriverWait(browser, 3).until(
|
||||
lambda b: not notebook.is_kernel_running(),
|
||||
message="Kernel did not shut down as expected"
|
||||
)
|
||||
|
||||
# Restart
|
||||
# Selenium can't click the menu while a modal dialog is fading out
|
||||
WebDriverWait(browser, 3).until(
|
||||
EC.invisibility_of_element((By.CSS_SELECTOR, '.modal-backdrop'))
|
||||
)
|
||||
kernel_menu.click()
|
||||
|
||||
wait_for_selector(browser, menu_item, visible=True, single=True).click()
|
||||
WebDriverWait(browser, 10).until(
|
||||
lambda b: notebook.is_kernel_running(),
|
||||
message="Restart (%r) after shutdown did not start kernel" % menu_item
|
||||
)
|
|
@ -0,0 +1,41 @@
|
|||
from nbformat.v4 import new_markdown_cell
|
||||
|
||||
def get_rendered_contents(nb):
|
||||
cl = ["text_cell", "render"]
|
||||
rendered_cells = [cell.find_element_by_class_name("text_cell_render")
|
||||
for cell in nb.cells
|
||||
if all([c in cell.get_attribute("class") for c in cl])]
|
||||
return [x.get_attribute('innerHTML').strip()
|
||||
for x in rendered_cells
|
||||
if x is not None]
|
||||
|
||||
|
||||
def test_markdown_cell(prefill_notebook):
|
||||
nb = prefill_notebook([new_markdown_cell(md) for md in [
|
||||
'# Foo', '**Bar**', '*Baz*', '```\nx = 1\n```', '```aaaa\nx = 1\n```',
|
||||
'```python\ns = "$"\nt = "$"\n```'
|
||||
]])
|
||||
|
||||
assert get_rendered_contents(nb) == [
|
||||
'<h1 id="Foo">Foo<a class="anchor-link" href="#Foo">¶</a></h1>',
|
||||
'<p><strong>Bar</strong></p>',
|
||||
'<p><em>Baz</em></p>',
|
||||
'<pre><code>x = 1</code></pre>',
|
||||
'<pre><code class="cm-s-ipython language-aaaa">x = 1</code></pre>',
|
||||
'<pre><code class="cm-s-ipython language-python">' +
|
||||
'<span class="cm-variable">s</span> <span class="cm-operator">=</span> <span class="cm-string">"$"</span>\n' +
|
||||
'<span class="cm-variable">t</span> <span class="cm-operator">=</span> <span class="cm-string">"$"</span></code></pre>'
|
||||
]
|
||||
|
||||
def test_markdown_headings(notebook):
|
||||
lst = list([1, 2, 3, 4, 5, 6, 2, 1])
|
||||
for i in lst:
|
||||
notebook.add_markdown_cell()
|
||||
cell_text = notebook.browser.execute_script(f"""
|
||||
var cell = IPython.notebook.get_cell(1);
|
||||
cell.set_heading_level({i});
|
||||
cell.get_text();
|
||||
""")
|
||||
assert notebook.get_cell_contents(1) == "#" * i + " "
|
||||
notebook.delete_cell(1)
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
"""Tests the merge cell api."""
|
||||
|
||||
INITIAL_CELLS = [
|
||||
"foo = 5",
|
||||
"bar = 10",
|
||||
"baz = 15",
|
||||
"print(foo)",
|
||||
"print(bar)",
|
||||
"print(baz)",
|
||||
]
|
||||
|
||||
def test_merge_cells(prefill_notebook):
|
||||
notebook = prefill_notebook(INITIAL_CELLS)
|
||||
a, b, c, d, e, f = INITIAL_CELLS
|
||||
|
||||
# Before merging, there are 6 separate cells
|
||||
assert notebook.get_cells_contents() == [a, b, c, d, e, f]
|
||||
|
||||
# Focus on the second cell and merge it with the cell above
|
||||
notebook.focus_cell(1)
|
||||
notebook.browser.execute_script("Jupyter.notebook.merge_cell_above();")
|
||||
merged_a_b = "%s\n\n%s" % (a, b)
|
||||
assert notebook.get_cells_contents() == [merged_a_b, c, d, e, f]
|
||||
|
||||
# Focus on the second cell and merge it with the cell below
|
||||
notebook.focus_cell(1)
|
||||
notebook.browser.execute_script("Jupyter.notebook.merge_cell_below();")
|
||||
merged_c_d = "%s\n\n%s" % (c, d)
|
||||
assert notebook.get_cells_contents() == [merged_a_b, merged_c_d, e, f]
|
||||
|
||||
# Merge everything down to a single cell with selected cells
|
||||
notebook.select_cell_range(0,3)
|
||||
notebook.browser.execute_script("Jupyter.notebook.merge_selected_cells();")
|
||||
merged_all = "%s\n\n%s\n\n%s\n\n%s" % (merged_a_b, merged_c_d, e, f)
|
||||
assert notebook.get_cells_contents() == [merged_all]
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
INITIAL_CELLS = ['1', '2', '3', '4', '5', '6']
|
||||
def test_move_multiselection(prefill_notebook):
|
||||
notebook = prefill_notebook(INITIAL_CELLS)
|
||||
def assert_oder(pre_message, expected_state):
|
||||
for i in range(len(expected_state)):
|
||||
assert expected_state[i] == notebook.get_cell_contents(i), f"{pre_message}: Verify that cell {i} has for content: {expected_state[i]} found: {notebook.get_cell_contents(i)}"
|
||||
|
||||
# Select 3 first cells
|
||||
notebook.select_cell_range(0, 2)
|
||||
notebook.browser.execute_script(
|
||||
"Jupyter.notebook.move_selection_up();"
|
||||
)
|
||||
# Should not move up at top
|
||||
assert_oder('move up at top', ['1', '2', '3', '4', '5','6'])
|
||||
|
||||
# We do not need to reselect, move/up down should keep the selection.
|
||||
notebook.browser.execute_script(
|
||||
"Jupyter.notebook.move_selection_down();"
|
||||
)
|
||||
notebook.browser.execute_script(
|
||||
"Jupyter.notebook.move_selection_down();"
|
||||
)
|
||||
notebook.browser.execute_script(
|
||||
"Jupyter.notebook.move_selection_down();"
|
||||
)
|
||||
|
||||
# 3 times down should move the 3 selected cells to the bottom
|
||||
assert_oder("move down to bottom", ['4', '5', '6', '1', '2', '3'])
|
||||
notebook.browser.execute_script(
|
||||
"Jupyter.notebook.move_selection_down();"
|
||||
)
|
||||
|
||||
# They can't go any futher
|
||||
assert_oder("move down to bottom", ['4', '5', '6', '1', '2', '3'])
|
||||
|
||||
notebook.browser.execute_script(
|
||||
"Jupyter.notebook.move_selection_up();"
|
||||
)
|
||||
notebook.browser.execute_script(
|
||||
"Jupyter.notebook.move_selection_up();"
|
||||
)
|
||||
notebook.browser.execute_script(
|
||||
"Jupyter.notebook.move_selection_up();"
|
||||
)
|
||||
|
||||
# Bring them back on top
|
||||
assert_oder('move up at top', ['1', '2', '3', '4', '5','6'])
|
|
@ -0,0 +1,63 @@
|
|||
INITIAL_CELLS = ['print("a")', 'print("b")', 'print("c")']
|
||||
|
||||
def test_multiselect(prefill_notebook):
|
||||
notebook = prefill_notebook(INITIAL_CELLS)
|
||||
|
||||
def extend_selection_by(delta):
|
||||
notebook.browser.execute_script(
|
||||
"Jupyter.notebook.extend_selection_by(arguments[0]);", delta)
|
||||
|
||||
def n_selected_cells():
|
||||
return notebook.browser.execute_script(
|
||||
"return Jupyter.notebook.get_selected_cells().length;")
|
||||
|
||||
notebook.focus_cell(0)
|
||||
assert n_selected_cells() == 1
|
||||
|
||||
# Check that only one cell is selected according to CSS classes as well
|
||||
selected_css = notebook.browser.find_elements_by_css_selector(
|
||||
'.cell.jupyter-soft-selected, .cell.selected')
|
||||
assert len(selected_css) == 1
|
||||
|
||||
# Extend the selection down one
|
||||
extend_selection_by(1)
|
||||
assert n_selected_cells() == 2
|
||||
|
||||
# Contract the selection up one
|
||||
extend_selection_by(-1)
|
||||
assert n_selected_cells() == 1
|
||||
|
||||
# Extend the selection up one
|
||||
notebook.focus_cell(1)
|
||||
extend_selection_by(-1)
|
||||
assert n_selected_cells() == 2
|
||||
|
||||
# Convert selected cells to Markdown
|
||||
notebook.browser.execute_script("Jupyter.notebook.cells_to_markdown();")
|
||||
cell_types = notebook.browser.execute_script(
|
||||
"return Jupyter.notebook.get_cells().map(c => c.cell_type)")
|
||||
assert cell_types == ['markdown', 'markdown', 'code']
|
||||
# One cell left selected after conversion
|
||||
assert n_selected_cells() == 1
|
||||
|
||||
# Convert selected cells to raw
|
||||
notebook.focus_cell(1)
|
||||
extend_selection_by(1)
|
||||
assert n_selected_cells() == 2
|
||||
notebook.browser.execute_script("Jupyter.notebook.cells_to_raw();")
|
||||
cell_types = notebook.browser.execute_script(
|
||||
"return Jupyter.notebook.get_cells().map(c => c.cell_type)")
|
||||
assert cell_types == ['markdown', 'raw', 'raw']
|
||||
# One cell left selected after conversion
|
||||
assert n_selected_cells() == 1
|
||||
|
||||
# Convert selected cells to code
|
||||
notebook.focus_cell(0)
|
||||
extend_selection_by(2)
|
||||
assert n_selected_cells() == 3
|
||||
notebook.browser.execute_script("Jupyter.notebook.cells_to_code();")
|
||||
cell_types = notebook.browser.execute_script(
|
||||
"return Jupyter.notebook.get_cells().map(c => c.cell_type)")
|
||||
assert cell_types == ['code'] * 3
|
||||
# One cell left selected after conversion
|
||||
assert n_selected_cells() == 1
|
|
@ -0,0 +1,44 @@
|
|||
|
||||
INITIAL_CELLS = ['print("a")', 'print("b")', 'print("c")']
|
||||
def test_multiselect_toggle(prefill_notebook):
|
||||
notebook = prefill_notebook(INITIAL_CELLS)
|
||||
def extend_selection_by(delta):
|
||||
notebook.browser.execute_script(
|
||||
"Jupyter.notebook.extend_selection_by(arguments[0]);", delta)
|
||||
|
||||
def n_selected_cells():
|
||||
return notebook.browser.execute_script(
|
||||
"return Jupyter.notebook.get_selected_cells().length;")
|
||||
|
||||
def select_cells():
|
||||
notebook.focus_cell(0)
|
||||
extend_selection_by(2)
|
||||
|
||||
# Test that cells, which start off not collapsed, are collapsed after
|
||||
# calling the multiselected cell toggle.
|
||||
select_cells()
|
||||
assert n_selected_cells() == 3
|
||||
notebook.browser.execute_script("Jupyter.notebook.execute_selected_cells();")
|
||||
select_cells()
|
||||
notebook.browser.execute_script("Jupyter.notebook.toggle_cells_outputs();")
|
||||
cell_output_states = notebook.browser.execute_script(
|
||||
"return Jupyter.notebook.get_cells().map(c => c.collapsed)")
|
||||
assert cell_output_states == [False] * 3, "ensure that all cells are not collapsed"
|
||||
|
||||
# Test that cells, which start off not scrolled are scrolled after
|
||||
# calling the multiselected scroll toggle.
|
||||
select_cells()
|
||||
assert n_selected_cells() == 3
|
||||
notebook.browser.execute_script("Jupyter.notebook.toggle_cells_outputs_scroll();")
|
||||
cell_scrolled_states = notebook.browser.execute_script(
|
||||
"return Jupyter.notebook.get_cells().map(c => c.output_area.scroll_state)")
|
||||
assert all(cell_scrolled_states), "ensure that all have scrolling enabled"
|
||||
|
||||
# Test that cells, which start off not cleared are cleared after
|
||||
# calling the multiselected scroll toggle.
|
||||
select_cells()
|
||||
assert n_selected_cells() == 3
|
||||
notebook.browser.execute_script("Jupyter.notebook.clear_cells_outputs();")
|
||||
cell_outputs_cleared = notebook.browser.execute_script(
|
||||
"return Jupyter.notebook.get_cells().map(c => c.output_area.element.html())")
|
||||
assert cell_outputs_cleared == [""] * 3, "ensure that all cells are cleared"
|
|
@ -0,0 +1,103 @@
|
|||
"""
|
||||
Test the notification area and widgets
|
||||
"""
|
||||
import pytest
|
||||
|
||||
from .utils import wait_for_selector, wait_for_script_to_return_true
|
||||
|
||||
|
||||
def get_widget(notebook, name):
|
||||
return notebook.browser.execute_script(
|
||||
f"return IPython.notification_area.get_widget('{name}') !== undefined"
|
||||
)
|
||||
|
||||
|
||||
def widget(notebook, name):
|
||||
return notebook.browser.execute_script(
|
||||
f"return IPython.notification_area.widget('{name}') !== undefined"
|
||||
)
|
||||
|
||||
|
||||
def new_notification_widget(notebook, name):
|
||||
return notebook.browser.execute_script(
|
||||
f"return IPython.notification_area.new_notification_widget('{name}') !== undefined"
|
||||
)
|
||||
|
||||
|
||||
def widget_has_class(notebook, name, class_name):
|
||||
return notebook.browser.execute_script(
|
||||
f"""
|
||||
var w = IPython.notification_area.get_widget('{name}');
|
||||
return w.element.hasClass('{class_name}');
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def widget_message(notebook, name):
|
||||
return notebook.browser.execute_script(
|
||||
f"""
|
||||
var w = IPython.notification_area.get_widget('{name}');
|
||||
return w.get_message();
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_notification(notebook):
|
||||
# check that existing widgets are there
|
||||
assert get_widget(notebook, "kernel") and widget(notebook, "kernel"),\
|
||||
"The kernel notification widget exists"
|
||||
assert get_widget(notebook, "notebook") and widget(notebook, "notebook"),\
|
||||
"The notebook notification widget exists"
|
||||
|
||||
# try getting a non-existent widget
|
||||
with pytest.raises(Exception):
|
||||
get_widget(notebook, "foo")
|
||||
|
||||
# try creating a non-existent widget
|
||||
assert widget(notebook, "bar"), "widget: new widget is created"
|
||||
|
||||
# try creating a widget that already exists
|
||||
with pytest.raises(Exception):
|
||||
new_notification_widget(notebook, "kernel")
|
||||
|
||||
# test creating 'info', 'warning' and 'danger' messages
|
||||
for level in ("info", "warning", "danger"):
|
||||
notebook.browser.execute_script(f"""
|
||||
var tnw = IPython.notification_area.widget('test');
|
||||
tnw.{level}('test {level}');
|
||||
""")
|
||||
wait_for_selector(notebook.browser, "#notification_test", visible=True)
|
||||
|
||||
assert widget_has_class(notebook, "test", level), f"{level}: class is correct"
|
||||
assert widget_message(notebook, "test") == f"test {level}", f"{level}: message is correct"
|
||||
|
||||
# test message timeout
|
||||
notebook.browser.execute_script("""
|
||||
var tnw = IPython.notification_area.widget('test');
|
||||
tnw.set_message('test timeout', 1000);
|
||||
""")
|
||||
wait_for_selector(notebook.browser, "#notification_test", visible=True)
|
||||
|
||||
assert widget_message(notebook, "test") == "test timeout", "timeout: message is correct"
|
||||
wait_for_selector(notebook.browser, "#notification_test", obscures=True)
|
||||
assert widget_message(notebook, "test") == "", "timeout: message was cleared"
|
||||
|
||||
# test click callback
|
||||
notebook.browser.execute_script("""
|
||||
var tnw = IPython.notification_area.widget('test');
|
||||
tnw._clicked = false;
|
||||
tnw.set_message('test click', undefined, function () {
|
||||
tnw._clicked = true;
|
||||
return true;
|
||||
});
|
||||
""")
|
||||
wait_for_selector(notebook.browser, "#notification_test", visible=True)
|
||||
|
||||
assert widget_message(notebook, "test") == "test click", "callback: message is correct"
|
||||
|
||||
notebook.browser.find_element_by_id("notification_test").click()
|
||||
wait_for_script_to_return_true(notebook.browser,
|
||||
'return IPython.notification_area.widget("test")._clicked;')
|
||||
wait_for_selector(notebook.browser, "#notification_test", obscures=True)
|
||||
|
||||
assert widget_message(notebook, "test") == "", "callback: message was cleared"
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
def test_prompt_numbers(prefill_notebook):
|
||||
notebook = prefill_notebook(['print("a")'])
|
||||
|
||||
def get_prompt():
|
||||
return (
|
||||
notebook.cells[0].find_element_by_class_name('input')
|
||||
.find_element_by_class_name('input_prompt')
|
||||
.get_attribute('innerHTML').strip()
|
||||
)
|
||||
|
||||
def set_prompt(value):
|
||||
notebook.set_cell_input_prompt(0, value)
|
||||
|
||||
assert get_prompt() == "<bdi>In</bdi> [ ]:"
|
||||
|
||||
set_prompt(2)
|
||||
assert get_prompt() == "<bdi>In</bdi> [2]:"
|
||||
|
||||
set_prompt(0)
|
||||
assert get_prompt() == "<bdi>In</bdi> [0]:"
|
||||
|
||||
set_prompt("'*'")
|
||||
assert get_prompt() == "<bdi>In</bdi> [*]:"
|
||||
|
||||
set_prompt("undefined")
|
||||
assert get_prompt() == "<bdi>In</bdi> [ ]:"
|
||||
|
||||
set_prompt("null")
|
||||
assert get_prompt() == "<bdi>In</bdi> [ ]:"
|
65
venv/Lib/site-packages/notebook/tests/selenium/test_save.py
Normal file
65
venv/Lib/site-packages/notebook/tests/selenium/test_save.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
"""Test saving a notebook with escaped characters
|
||||
"""
|
||||
|
||||
from urllib.parse import quote
|
||||
from .utils import wait_for_selector
|
||||
|
||||
promise_js = """
|
||||
var done = arguments[arguments.length - 1];
|
||||
%s.then(
|
||||
data => { done(["success", data]); },
|
||||
error => { done(["error", error]); }
|
||||
);
|
||||
"""
|
||||
|
||||
def execute_promise(js, browser):
|
||||
state, data = browser.execute_async_script(promise_js % js)
|
||||
if state == 'success':
|
||||
return data
|
||||
raise Exception(data)
|
||||
|
||||
|
||||
def test_save(notebook):
|
||||
# don't use unicode with ambiguous composed/decomposed normalization
|
||||
# because the filesystem may use a different normalization than literals.
|
||||
# This causes no actual problems, but will break string comparison.
|
||||
nbname = "has#hash and space and unicø∂e.ipynb"
|
||||
escaped_name = quote(nbname)
|
||||
|
||||
notebook.edit_cell(index=0, content="s = '??'")
|
||||
|
||||
notebook.browser.execute_script("Jupyter.notebook.set_notebook_name(arguments[0])", nbname)
|
||||
|
||||
model = execute_promise("Jupyter.notebook.save_notebook()", notebook.browser)
|
||||
assert model['name'] == nbname
|
||||
|
||||
current_name = notebook.browser.execute_script("return Jupyter.notebook.notebook_name")
|
||||
assert current_name == nbname
|
||||
|
||||
current_path = notebook.browser.execute_script("return Jupyter.notebook.notebook_path")
|
||||
assert current_path == nbname
|
||||
|
||||
displayed_name = notebook.browser.find_element_by_id('notebook_name').text
|
||||
assert displayed_name + '.ipynb' == nbname
|
||||
|
||||
execute_promise("Jupyter.notebook.save_checkpoint()", notebook.browser)
|
||||
|
||||
checkpoints = notebook.browser.execute_script("return Jupyter.notebook.checkpoints")
|
||||
assert len(checkpoints) == 1
|
||||
|
||||
notebook.browser.find_element_by_css_selector('#ipython_notebook a').click()
|
||||
hrefs_nonmatch = []
|
||||
for link in wait_for_selector(notebook.browser, 'a.item_link'):
|
||||
href = link.get_attribute('href')
|
||||
if escaped_name in href:
|
||||
print("Opening", href)
|
||||
notebook.browser.get(href)
|
||||
wait_for_selector(notebook.browser, '.cell')
|
||||
break
|
||||
hrefs_nonmatch.append(href)
|
||||
else:
|
||||
raise AssertionError("{!r} not found in {!r}"
|
||||
.format(escaped_name, hrefs_nonmatch))
|
||||
|
||||
current_name = notebook.browser.execute_script("return Jupyter.notebook.notebook_name")
|
||||
assert current_name == nbname
|
|
@ -0,0 +1,40 @@
|
|||
from notebook.tests.selenium.utils import wait_for_selector
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
|
||||
def wait_for_rename(browser, nbname, timeout=10):
|
||||
wait = WebDriverWait(browser, timeout)
|
||||
def notebook_renamed(browser):
|
||||
elem = browser.find_element_by_id('notebook_name')
|
||||
current_name = browser.execute_script('return arguments[0].innerText', elem)
|
||||
return current_name == nbname
|
||||
return wait.until(notebook_renamed)
|
||||
|
||||
def save_as(nb):
|
||||
JS = 'Jupyter.notebook.save_notebook_as()'
|
||||
return nb.browser.execute_script(JS)
|
||||
|
||||
def get_notebook_name(nb):
|
||||
JS = 'return Jupyter.notebook.notebook_name'
|
||||
return nb.browser.execute_script(JS)
|
||||
|
||||
def set_notebook_name(nb, name):
|
||||
JS = 'Jupyter.notebook.rename("{}")'.format(name)
|
||||
nb.browser.execute_script(JS)
|
||||
|
||||
def test_save_notebook_as(notebook):
|
||||
# Set a name for comparison later
|
||||
set_notebook_name(notebook, name="nb1.ipynb")
|
||||
wait_for_rename(notebook.browser, "nb1")
|
||||
assert get_notebook_name(notebook) == "nb1.ipynb"
|
||||
# Wait for Save As modal, save
|
||||
save_as(notebook)
|
||||
wait_for_selector(notebook.browser, '.save-message')
|
||||
inp = notebook.browser.find_element_by_xpath('//input[@data-testid="save-as"]')
|
||||
inp.send_keys('new_notebook.ipynb')
|
||||
inp.send_keys(Keys.RETURN)
|
||||
wait_for_rename(notebook.browser, "new_notebook")
|
||||
# Test that the name changed
|
||||
assert get_notebook_name(notebook) == "new_notebook.ipynb"
|
||||
# Test that address bar was updated (TODO: get the base url)
|
||||
assert "new_notebook.ipynb" in notebook.browser.current_url
|
|
@ -0,0 +1,80 @@
|
|||
from notebook.tests.selenium.utils import wait_for_selector, Notebook
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
|
||||
|
||||
promise_js = """
|
||||
var done = arguments[arguments.length - 1];
|
||||
(%s).then(
|
||||
data => { done(["success", data]); },
|
||||
error => { done(["error", error]); }
|
||||
);
|
||||
"""
|
||||
|
||||
def execute_promise(js, browser):
|
||||
state, data = browser.execute_async_script(promise_js % js)
|
||||
if state == 'success':
|
||||
return data
|
||||
raise Exception(data)
|
||||
|
||||
def wait_for_rename(browser, nbname, timeout=10):
|
||||
wait = WebDriverWait(browser, timeout)
|
||||
def notebook_renamed(browser):
|
||||
elem = browser.find_element_by_id('notebook_name')
|
||||
current_name = browser.execute_script('return arguments[0].innerText', elem)
|
||||
return current_name == nbname
|
||||
return wait.until(notebook_renamed)
|
||||
|
||||
def save_as(nb):
|
||||
JS = 'Jupyter.notebook.save_notebook_as()'
|
||||
return nb.browser.execute_script(JS)
|
||||
|
||||
def get_notebook_name(nb):
|
||||
JS = 'return Jupyter.notebook.notebook_name'
|
||||
return nb.browser.execute_script(JS)
|
||||
|
||||
def refresh_notebook(nb):
|
||||
nb.browser.refresh()
|
||||
nb.__init__(nb.browser)
|
||||
|
||||
def test_save_readonly_notebook_as(notebook):
|
||||
# Make notebook read-only
|
||||
notebook.edit_cell(index=0, content='import os\nimport stat\nos.chmod("'
|
||||
+ notebook.browser.current_url.split('?')[0].split('/')[-1] + '", stat.S_IREAD)\nprint(0)')
|
||||
notebook.browser.execute_script("Jupyter.notebook.get_cell(0).execute();")
|
||||
notebook.wait_for_cell_output(0)
|
||||
refresh_notebook(notebook)
|
||||
# Test that the notebook is read-only
|
||||
assert notebook.browser.execute_script('return Jupyter.notebook.writable') == False
|
||||
|
||||
# Add some content
|
||||
test_content_0 = "print('a simple')\nprint('test script')"
|
||||
notebook.edit_cell(index=0, content=test_content_0)
|
||||
|
||||
# Wait for Save As modal, save
|
||||
save_as(notebook)
|
||||
wait_for_selector(notebook.browser, '.save-message')
|
||||
inp = notebook.browser.find_element_by_xpath('//input[@data-testid="save-as"]')
|
||||
inp.send_keys('writable_notebook.ipynb')
|
||||
inp.send_keys(Keys.RETURN)
|
||||
wait_for_rename(notebook.browser, "writable_notebook")
|
||||
# Test that the name changed
|
||||
assert get_notebook_name(notebook) == "writable_notebook.ipynb"
|
||||
# Test that address bar was updated (TODO: get the base url)
|
||||
assert "writable_notebook.ipynb" in notebook.browser.current_url
|
||||
# Test that it is no longer read-only
|
||||
assert notebook.browser.execute_script('return Jupyter.notebook.writable') == True
|
||||
|
||||
# Add some more content
|
||||
test_content_1 = "print('a second simple')\nprint('script to test save feature')"
|
||||
notebook.add_and_execute_cell(content=test_content_1)
|
||||
# and save the notebook
|
||||
execute_promise("Jupyter.notebook.save_notebook()", notebook.browser)
|
||||
|
||||
# Test that it still contains the content
|
||||
assert notebook.get_cell_contents(index=0) == test_content_0
|
||||
assert notebook.get_cell_contents(index=1) == test_content_1
|
||||
# even after a refresh
|
||||
refresh_notebook(notebook)
|
||||
assert notebook.get_cell_contents(index=0) == test_content_0
|
||||
assert notebook.get_cell_contents(index=1) == test_content_1
|
|
@ -0,0 +1,15 @@
|
|||
"""Tests shutdown of the Kernel."""
|
||||
from .utils import wait_for_selector, wait_for_xpath
|
||||
|
||||
def test_shutdown(notebook):
|
||||
notebook.edit_cell(content="print(21)")
|
||||
wait_for_xpath(notebook.browser, '//a[text()="Kernel"]', single=True).click()
|
||||
wait_for_selector(notebook.browser, '#shutdown_kernel', single=True).click()
|
||||
wait_for_selector(notebook.browser, '.btn.btn-default.btn-sm.btn-danger', single=True).click()
|
||||
|
||||
#Wait until all shutdown modal elements disappear before trying to execute the cell
|
||||
wait_for_xpath(notebook.browser, "//div[contains(@class,'modal')]", obscures=True)
|
||||
notebook.execute_cell(0)
|
||||
|
||||
assert not notebook.is_kernel_running()
|
||||
assert len(notebook.get_cell_output()) == 0
|
|
@ -0,0 +1,92 @@
|
|||
from selenium.webdriver.common.keys import Keys
|
||||
from .utils import shift
|
||||
|
||||
def undelete(nb):
|
||||
nb.browser.execute_script('Jupyter.notebook.undelete_cell();')
|
||||
|
||||
INITIAL_CELLS = ['print("a")', 'print("b")', 'print("c")', 'print("d")']
|
||||
|
||||
def test_undelete_cells(prefill_notebook):
|
||||
notebook = prefill_notebook(INITIAL_CELLS)
|
||||
a, b, c, d = INITIAL_CELLS
|
||||
|
||||
# Verify initial state
|
||||
assert notebook.get_cells_contents() == [a, b, c, d]
|
||||
|
||||
# Delete cells [1, 2]
|
||||
notebook.focus_cell(1)
|
||||
shift(notebook.browser, Keys.DOWN)
|
||||
notebook.current_cell.send_keys('dd')
|
||||
assert notebook.get_cells_contents() == [a, d]
|
||||
|
||||
# Delete new cell 1 (which contains d)
|
||||
notebook.focus_cell(1)
|
||||
notebook.current_cell.send_keys('dd')
|
||||
assert notebook.get_cells_contents() == [a]
|
||||
|
||||
# Undelete d
|
||||
undelete(notebook)
|
||||
assert notebook.get_cells_contents() == [a, d]
|
||||
|
||||
# Undelete b, c
|
||||
undelete(notebook)
|
||||
assert notebook.get_cells_contents() == [a, b, c, d]
|
||||
|
||||
# Nothing more to undelete
|
||||
undelete(notebook)
|
||||
assert notebook.get_cells_contents() == [a, b, c, d]
|
||||
|
||||
# Delete first two cells and restore
|
||||
notebook.focus_cell(0)
|
||||
shift(notebook.browser, Keys.DOWN)
|
||||
notebook.current_cell.send_keys('dd')
|
||||
assert notebook.get_cells_contents() == [c, d]
|
||||
undelete(notebook)
|
||||
assert notebook.get_cells_contents() == [a, b, c, d]
|
||||
|
||||
# Delete last two cells and restore
|
||||
notebook.focus_cell(-1)
|
||||
shift(notebook.browser, Keys.UP)
|
||||
notebook.current_cell.send_keys('dd')
|
||||
assert notebook.get_cells_contents() == [a, b]
|
||||
undelete(notebook)
|
||||
assert notebook.get_cells_contents() == [a, b, c, d]
|
||||
|
||||
# Merge cells [1, 2], restore the deleted one
|
||||
bc = b + "\n\n" + c
|
||||
notebook.focus_cell(1)
|
||||
shift(notebook.browser, 'j')
|
||||
shift(notebook.browser, 'm')
|
||||
assert notebook.get_cells_contents() == [a, bc, d]
|
||||
undelete(notebook)
|
||||
assert notebook.get_cells_contents() == [a, bc, c, d]
|
||||
|
||||
# Merge cells [2, 3], restore the deleted one
|
||||
cd = c + "\n\n" + d
|
||||
notebook.focus_cell(-1)
|
||||
shift(notebook.browser, 'k')
|
||||
shift(notebook.browser, 'm')
|
||||
assert notebook.get_cells_contents() == [a, bc, cd]
|
||||
undelete(notebook)
|
||||
assert notebook.get_cells_contents() == [a, bc, cd, d]
|
||||
|
||||
# Reset contents to [a, b, c, d] --------------------------------------
|
||||
notebook.edit_cell(index=1, content=b)
|
||||
notebook.edit_cell(index=2, content=c)
|
||||
assert notebook.get_cells_contents() == [a, b, c, d]
|
||||
|
||||
# Merge cell below, restore the deleted one
|
||||
ab = a + "\n\n" + b
|
||||
notebook.focus_cell(0)
|
||||
notebook.browser.execute_script("Jupyter.notebook.merge_cell_below();")
|
||||
assert notebook.get_cells_contents() == [ab, c, d]
|
||||
undelete(notebook)
|
||||
assert notebook.get_cells_contents() == [ab, b, c, d]
|
||||
|
||||
# Merge cell above, restore the deleted one
|
||||
cd = c + "\n\n" + d
|
||||
notebook.focus_cell(-1)
|
||||
notebook.browser.execute_script("Jupyter.notebook.merge_cell_above();")
|
||||
assert notebook.get_cells_contents() == [ab, b, cd]
|
||||
undelete(notebook)
|
||||
assert notebook.get_cells_contents() == [ab, b, c, cd]
|
469
venv/Lib/site-packages/notebook/tests/selenium/utils.py
Normal file
469
venv/Lib/site-packages/notebook/tests/selenium/utils.py
Normal file
|
@ -0,0 +1,469 @@
|
|||
import os
|
||||
from selenium.webdriver import ActionChains
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.webdriver.remote.webelement import WebElement
|
||||
|
||||
from contextlib import contextmanager
|
||||
|
||||
pjoin = os.path.join
|
||||
|
||||
|
||||
def wait_for_selector(driver, selector, timeout=10, visible=False, single=False, wait_for_n=1, obscures=False):
|
||||
if wait_for_n > 1:
|
||||
return _wait_for_multiple(
|
||||
driver, By.CSS_SELECTOR, selector, timeout, wait_for_n, visible)
|
||||
return _wait_for(driver, By.CSS_SELECTOR, selector, timeout, visible, single, obscures)
|
||||
|
||||
|
||||
def wait_for_tag(driver, tag, timeout=10, visible=False, single=False, wait_for_n=1, obscures=False):
|
||||
if wait_for_n > 1:
|
||||
return _wait_for_multiple(
|
||||
driver, By.TAG_NAME, tag, timeout, wait_for_n, visible)
|
||||
return _wait_for(driver, By.TAG_NAME, tag, timeout, visible, single, obscures)
|
||||
|
||||
|
||||
def wait_for_xpath(driver, xpath, timeout=10, visible=False, single=False, wait_for_n=1, obscures=False):
|
||||
if wait_for_n > 1:
|
||||
return _wait_for_multiple(
|
||||
driver, By.XPATH, xpath, timeout, wait_for_n, visible)
|
||||
return _wait_for(driver, By.XPATH, xpath, timeout, visible, single, obscures)
|
||||
|
||||
|
||||
def wait_for_script_to_return_true(driver, script, timeout=10):
|
||||
WebDriverWait(driver, timeout).until(lambda d: d.execute_script(script))
|
||||
|
||||
|
||||
def _wait_for(driver, locator_type, locator, timeout=10, visible=False, single=False, obscures=False):
|
||||
"""Waits `timeout` seconds for the specified condition to be met. Condition is
|
||||
met if any matching element is found. Returns located element(s) when found.
|
||||
|
||||
Args:
|
||||
driver: Selenium web driver instance
|
||||
locator_type: type of locator (e.g. By.CSS_SELECTOR or By.TAG_NAME)
|
||||
locator: name of tag, class, etc. to wait for
|
||||
timeout: how long to wait for presence/visibility of element
|
||||
visible: if True, require that element is not only present, but visible
|
||||
single: if True, return a single element, otherwise return a list of matching
|
||||
elements
|
||||
obscures: if True, waits until the element becomes invisible
|
||||
"""
|
||||
wait = WebDriverWait(driver, timeout)
|
||||
if obscures:
|
||||
conditional = EC.invisibility_of_element_located
|
||||
elif single:
|
||||
if visible:
|
||||
conditional = EC.visibility_of_element_located
|
||||
else:
|
||||
conditional = EC.presence_of_element_located
|
||||
else:
|
||||
if visible:
|
||||
conditional = EC.visibility_of_all_elements_located
|
||||
else:
|
||||
conditional = EC.presence_of_all_elements_located
|
||||
return wait.until(conditional((locator_type, locator)))
|
||||
|
||||
|
||||
def _wait_for_multiple(driver, locator_type, locator, timeout, wait_for_n, visible=False):
|
||||
"""Waits until `wait_for_n` matching elements to be present (or visible).
|
||||
Returns located elements when found.
|
||||
|
||||
Args:
|
||||
driver: Selenium web driver instance
|
||||
locator_type: type of locator (e.g. By.CSS_SELECTOR or By.TAG_NAME)
|
||||
locator: name of tag, class, etc. to wait for
|
||||
timeout: how long to wait for presence/visibility of element
|
||||
wait_for_n: wait until this number of matching elements are present/visible
|
||||
visible: if True, require that elements are not only present, but visible
|
||||
"""
|
||||
wait = WebDriverWait(driver, timeout)
|
||||
|
||||
def multiple_found(driver):
|
||||
elements = driver.find_elements(locator_type, locator)
|
||||
if visible:
|
||||
elements = [e for e in elements if e.is_displayed()]
|
||||
if len(elements) < wait_for_n:
|
||||
return False
|
||||
return elements
|
||||
|
||||
return wait.until(multiple_found)
|
||||
|
||||
|
||||
class CellTypeError(ValueError):
|
||||
|
||||
def __init__(self, message=""):
|
||||
self.message = message
|
||||
|
||||
|
||||
class Notebook:
|
||||
|
||||
def __init__(self, browser):
|
||||
self.browser = browser
|
||||
self._wait_for_start()
|
||||
self.disable_autosave_and_onbeforeunload()
|
||||
|
||||
def __len__(self):
|
||||
return len(self.cells)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.cells[key]
|
||||
|
||||
def __setitem__(self, key, item):
|
||||
if isinstance(key, int):
|
||||
self.edit_cell(index=key, content=item, render=False)
|
||||
# TODO: re-add slicing support, handle general python slicing behaviour
|
||||
# includes: overwriting the entire self.cells object if you do
|
||||
# self[:] = []
|
||||
# elif isinstance(key, slice):
|
||||
# indices = (self.index(cell) for cell in self[key])
|
||||
# for k, v in zip(indices, item):
|
||||
# self.edit_cell(index=k, content=v, render=False)
|
||||
|
||||
def __iter__(self):
|
||||
return (cell for cell in self.cells)
|
||||
|
||||
def _wait_for_start(self):
|
||||
"""Wait until the notebook interface is loaded and the kernel started"""
|
||||
wait_for_selector(self.browser, '.cell')
|
||||
WebDriverWait(self.browser, 10).until(
|
||||
lambda drvr: self.is_kernel_running()
|
||||
)
|
||||
|
||||
@property
|
||||
def body(self):
|
||||
return self.browser.find_element_by_tag_name("body")
|
||||
|
||||
@property
|
||||
def cells(self):
|
||||
"""Gets all cells once they are visible.
|
||||
|
||||
"""
|
||||
return self.browser.find_elements_by_class_name("cell")
|
||||
|
||||
@property
|
||||
def current_index(self):
|
||||
return self.index(self.current_cell)
|
||||
|
||||
def index(self, cell):
|
||||
return self.cells.index(cell)
|
||||
|
||||
def disable_autosave_and_onbeforeunload(self):
|
||||
"""Disable request to save before closing window and autosave.
|
||||
|
||||
This is most easily done by using js directly.
|
||||
"""
|
||||
self.browser.execute_script("window.onbeforeunload = null;")
|
||||
self.browser.execute_script("Jupyter.notebook.set_autosave_interval(0)")
|
||||
|
||||
def to_command_mode(self):
|
||||
"""Changes us into command mode on currently focused cell
|
||||
|
||||
"""
|
||||
self.body.send_keys(Keys.ESCAPE)
|
||||
self.browser.execute_script("return Jupyter.notebook.handle_command_mode("
|
||||
"Jupyter.notebook.get_cell("
|
||||
"Jupyter.notebook.get_edit_index()))")
|
||||
|
||||
def focus_cell(self, index=0):
|
||||
cell = self.cells[index]
|
||||
cell.click()
|
||||
self.to_command_mode()
|
||||
self.current_cell = cell
|
||||
|
||||
def select_cell_range(self, initial_index=0, final_index=0):
|
||||
self.focus_cell(initial_index)
|
||||
self.to_command_mode()
|
||||
for i in range(final_index - initial_index):
|
||||
shift(self.browser, 'j')
|
||||
|
||||
def find_and_replace(self, index=0, find_txt='', replace_txt=''):
|
||||
self.focus_cell(index)
|
||||
self.to_command_mode()
|
||||
self.body.send_keys('f')
|
||||
wait_for_selector(self.browser, "#find-and-replace", single=True)
|
||||
self.browser.find_element_by_id("findreplace_allcells_btn").click()
|
||||
self.browser.find_element_by_id("findreplace_find_inp").send_keys(find_txt)
|
||||
self.browser.find_element_by_id("findreplace_replace_inp").send_keys(replace_txt)
|
||||
self.browser.find_element_by_id("findreplace_replaceall_btn").click()
|
||||
|
||||
def convert_cell_type(self, index=0, cell_type="code"):
|
||||
# TODO add check to see if it is already present
|
||||
self.focus_cell(index)
|
||||
cell = self.cells[index]
|
||||
if cell_type == "markdown":
|
||||
self.current_cell.send_keys("m")
|
||||
elif cell_type == "raw":
|
||||
self.current_cell.send_keys("r")
|
||||
elif cell_type == "code":
|
||||
self.current_cell.send_keys("y")
|
||||
else:
|
||||
raise CellTypeError(("{} is not a valid cell type,"
|
||||
"use 'code', 'markdown', or 'raw'").format(cell_type))
|
||||
|
||||
self.wait_for_stale_cell(cell)
|
||||
self.focus_cell(index)
|
||||
return self.current_cell
|
||||
|
||||
def wait_for_stale_cell(self, cell):
|
||||
""" This is needed to switch a cell's mode and refocus it, or to render it.
|
||||
|
||||
Warning: there is currently no way to do this when changing between
|
||||
markdown and raw cells.
|
||||
"""
|
||||
wait = WebDriverWait(self.browser, 10)
|
||||
element = wait.until(EC.staleness_of(cell))
|
||||
|
||||
def wait_for_element_availability(self, element):
|
||||
_wait_for(self.browser, By.CLASS_NAME, element, visible=True)
|
||||
|
||||
def get_cells_contents(self):
|
||||
JS = 'return Jupyter.notebook.get_cells().map(function(c) {return c.get_text();})'
|
||||
return self.browser.execute_script(JS)
|
||||
|
||||
def get_cell_contents(self, index=0, selector='div .CodeMirror-code'):
|
||||
return self.cells[index].find_element_by_css_selector(selector).text
|
||||
|
||||
def get_cell_output(self, index=0, output='output_subarea'):
|
||||
return self.cells[index].find_elements_by_class_name(output)
|
||||
|
||||
def wait_for_cell_output(self, index=0, timeout=10):
|
||||
return WebDriverWait(self.browser, timeout).until(
|
||||
lambda b: self.get_cell_output(index)
|
||||
)
|
||||
|
||||
def set_cell_metadata(self, index, key, value):
|
||||
JS = 'Jupyter.notebook.get_cell({}).metadata.{} = {}'.format(index, key, value)
|
||||
return self.browser.execute_script(JS)
|
||||
|
||||
def get_cell_type(self, index=0):
|
||||
JS = 'return Jupyter.notebook.get_cell({}).cell_type'.format(index)
|
||||
return self.browser.execute_script(JS)
|
||||
|
||||
def set_cell_input_prompt(self, index, prmpt_val):
|
||||
JS = 'Jupyter.notebook.get_cell({}).set_input_prompt({})'.format(index, prmpt_val)
|
||||
self.browser.execute_script(JS)
|
||||
|
||||
def edit_cell(self, cell=None, index=0, content="", render=False):
|
||||
"""Set the contents of a cell to *content*, by cell object or by index
|
||||
"""
|
||||
if cell is not None:
|
||||
index = self.index(cell)
|
||||
self.focus_cell(index)
|
||||
|
||||
# Select & delete anything already in the cell
|
||||
self.current_cell.send_keys(Keys.ENTER)
|
||||
cmdtrl(self.browser, 'a')
|
||||
self.current_cell.send_keys(Keys.DELETE)
|
||||
|
||||
for line_no, line in enumerate(content.splitlines()):
|
||||
if line_no != 0:
|
||||
self.current_cell.send_keys(Keys.ENTER, "\n")
|
||||
self.current_cell.send_keys(Keys.ENTER, line)
|
||||
if render:
|
||||
self.execute_cell(self.current_index)
|
||||
|
||||
def execute_cell(self, cell_or_index=None):
|
||||
if isinstance(cell_or_index, int):
|
||||
index = cell_or_index
|
||||
elif isinstance(cell_or_index, WebElement):
|
||||
index = self.index(cell_or_index)
|
||||
else:
|
||||
raise TypeError("execute_cell only accepts a WebElement or an int")
|
||||
self.focus_cell(index)
|
||||
self.current_cell.send_keys(Keys.CONTROL, Keys.ENTER)
|
||||
|
||||
def add_cell(self, index=-1, cell_type="code", content=""):
|
||||
self.focus_cell(index)
|
||||
self.current_cell.send_keys("b")
|
||||
new_index = index + 1 if index >= 0 else index
|
||||
if content:
|
||||
self.edit_cell(index=index, content=content)
|
||||
if cell_type != 'code':
|
||||
self.convert_cell_type(index=new_index, cell_type=cell_type)
|
||||
|
||||
def add_and_execute_cell(self, index=-1, cell_type="code", content=""):
|
||||
self.add_cell(index=index, cell_type=cell_type, content=content)
|
||||
self.execute_cell(index)
|
||||
|
||||
def delete_cell(self, index):
|
||||
self.focus_cell(index)
|
||||
self.to_command_mode()
|
||||
self.current_cell.send_keys('dd')
|
||||
|
||||
def add_markdown_cell(self, index=-1, content="", render=True):
|
||||
self.add_cell(index, cell_type="markdown")
|
||||
self.edit_cell(index=index, content=content, render=render)
|
||||
|
||||
def append(self, *values, cell_type="code"):
|
||||
for i, value in enumerate(values):
|
||||
if isinstance(value, str):
|
||||
self.add_cell(cell_type=cell_type,
|
||||
content=value)
|
||||
else:
|
||||
raise TypeError("Don't know how to add cell from %r" % value)
|
||||
|
||||
def extend(self, values):
|
||||
self.append(*values)
|
||||
|
||||
def run_all(self):
|
||||
for cell in self:
|
||||
self.execute_cell(cell)
|
||||
|
||||
def trigger_keydown(self, keys):
|
||||
trigger_keystrokes(self.body, keys)
|
||||
|
||||
def is_kernel_running(self):
|
||||
return self.browser.execute_script(
|
||||
"return Jupyter.notebook.kernel && Jupyter.notebook.kernel.is_connected()"
|
||||
)
|
||||
|
||||
def clear_cell_output(self, index):
|
||||
JS = 'Jupyter.notebook.clear_output({})'.format(index)
|
||||
self.browser.execute_script(JS)
|
||||
|
||||
@classmethod
|
||||
def new_notebook(cls, browser, kernel_name='kernel-python3'):
|
||||
with new_window(browser):
|
||||
select_kernel(browser, kernel_name=kernel_name)
|
||||
return cls(browser)
|
||||
|
||||
|
||||
def select_kernel(browser, kernel_name='kernel-python3'):
|
||||
"""Clicks the "new" button and selects a kernel from the options.
|
||||
"""
|
||||
wait = WebDriverWait(browser, 10)
|
||||
new_button = wait.until(EC.element_to_be_clickable((By.ID, "new-dropdown-button")))
|
||||
new_button.click()
|
||||
kernel_selector = '#{} a'.format(kernel_name)
|
||||
kernel = wait_for_selector(browser, kernel_selector, single=True)
|
||||
kernel.click()
|
||||
|
||||
|
||||
@contextmanager
|
||||
def new_window(browser):
|
||||
"""Contextmanager for switching to & waiting for a window created.
|
||||
|
||||
This context manager gives you the ability to create a new window inside
|
||||
the created context and it will switch you to that new window.
|
||||
|
||||
Usage example:
|
||||
|
||||
from notebook.tests.selenium.utils import new_window, Notebook
|
||||
|
||||
⋮ # something that creates a browser object
|
||||
|
||||
with new_window(browser):
|
||||
select_kernel(browser, kernel_name=kernel_name)
|
||||
nb = Notebook(browser)
|
||||
|
||||
"""
|
||||
initial_window_handles = browser.window_handles
|
||||
yield
|
||||
new_window_handles = [window for window in browser.window_handles
|
||||
if window not in initial_window_handles]
|
||||
if not new_window_handles:
|
||||
raise Exception("No new windows opened during context")
|
||||
browser.switch_to.window(new_window_handles[0])
|
||||
|
||||
def shift(browser, k):
|
||||
"""Send key combination Shift+(k)"""
|
||||
trigger_keystrokes(browser, "shift-%s"%k)
|
||||
|
||||
def cmdtrl(browser, k):
|
||||
"""Send key combination Ctrl+(k) or Command+(k) for MacOS"""
|
||||
trigger_keystrokes(browser, "command-%s"%k) if os.uname()[0] == "Darwin" else trigger_keystrokes(browser, "control-%s"%k)
|
||||
|
||||
def alt(browser, k):
|
||||
"""Send key combination Alt+(k)"""
|
||||
trigger_keystrokes(browser, 'alt-%s'%k)
|
||||
|
||||
def trigger_keystrokes(browser, *keys):
|
||||
""" Send the keys in sequence to the browser.
|
||||
Handles following key combinations
|
||||
1. with modifiers eg. 'control-alt-a', 'shift-c'
|
||||
2. just modifiers eg. 'alt', 'esc'
|
||||
3. non-modifiers eg. 'abc'
|
||||
Modifiers : http://seleniumhq.github.io/selenium/docs/api/py/webdriver/selenium.webdriver.common.keys.html
|
||||
"""
|
||||
for each_key_combination in keys:
|
||||
keys = each_key_combination.split('-')
|
||||
if len(keys) > 1: # key has modifiers eg. control, alt, shift
|
||||
modifiers_keys = [getattr(Keys, x.upper()) for x in keys[:-1]]
|
||||
ac = ActionChains(browser)
|
||||
for i in modifiers_keys: ac = ac.key_down(i)
|
||||
ac.send_keys(keys[-1])
|
||||
for i in modifiers_keys[::-1]: ac = ac.key_up(i)
|
||||
ac.perform()
|
||||
else: # single key stroke. Check if modifier eg. "up"
|
||||
browser.send_keys(getattr(Keys, keys[0].upper(), keys[0]))
|
||||
|
||||
def validate_dualmode_state(notebook, mode, index):
|
||||
'''Validate the entire dual mode state of the notebook.
|
||||
Checks if the specified cell is selected, and the mode and keyboard mode are the same.
|
||||
Depending on the mode given:
|
||||
Command: Checks that no cells are in focus or in edit mode.
|
||||
Edit: Checks that only the specified cell is in focus and in edit mode.
|
||||
'''
|
||||
def is_only_cell_edit(index):
|
||||
JS = 'return Jupyter.notebook.get_cells().map(function(c) {return c.mode;})'
|
||||
cells_mode = notebook.browser.execute_script(JS)
|
||||
#None of the cells are in edit mode
|
||||
if index is None:
|
||||
for mode in cells_mode:
|
||||
if mode == 'edit':
|
||||
return False
|
||||
return True
|
||||
#Only the index cell is on edit mode
|
||||
for i, mode in enumerate(cells_mode):
|
||||
if i == index:
|
||||
if mode != 'edit':
|
||||
return False
|
||||
else:
|
||||
if mode == 'edit':
|
||||
return False
|
||||
return True
|
||||
|
||||
def is_focused_on(index):
|
||||
JS = "return $('#notebook .CodeMirror-focused textarea').length;"
|
||||
focused_cells = notebook.browser.execute_script(JS)
|
||||
if index is None:
|
||||
return focused_cells == 0
|
||||
|
||||
if focused_cells != 1: #only one cell is focused
|
||||
return False
|
||||
|
||||
JS = "return $('#notebook .CodeMirror-focused textarea')[0];"
|
||||
focused_cell = notebook.browser.execute_script(JS)
|
||||
JS = "return IPython.notebook.get_cell(%s).code_mirror.getInputField()"%index
|
||||
cell = notebook.browser.execute_script(JS)
|
||||
return focused_cell == cell
|
||||
|
||||
|
||||
#general test
|
||||
JS = "return IPython.keyboard_manager.mode;"
|
||||
keyboard_mode = notebook.browser.execute_script(JS)
|
||||
JS = "return IPython.notebook.mode;"
|
||||
notebook_mode = notebook.browser.execute_script(JS)
|
||||
|
||||
#validate selected cell
|
||||
JS = "return Jupyter.notebook.get_selected_cells_indices();"
|
||||
cell_index = notebook.browser.execute_script(JS)
|
||||
assert cell_index == [index] #only the index cell is selected
|
||||
|
||||
if mode != 'command' and mode != 'edit':
|
||||
raise Exception('An unknown mode was send: mode = "%s"'%mode) #An unknown mode is send
|
||||
|
||||
#validate mode
|
||||
assert mode == keyboard_mode #keyboard mode is correct
|
||||
|
||||
if mode == 'command':
|
||||
assert is_focused_on(None) #no focused cells
|
||||
|
||||
assert is_only_cell_edit(None) #no cells in edit mode
|
||||
|
||||
elif mode == 'edit':
|
||||
assert is_focused_on(index) #The specified cell is focused
|
||||
|
||||
assert is_only_cell_edit(index) #The specified cell is the only one in edit mode
|
Loading…
Add table
Add a link
Reference in a new issue