Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Selenium utils + markdown rendering tests #3458

Merged
merged 40 commits into from
Mar 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
9d4cf94
add utils module and wait_for_selector
mpacer Mar 20, 2018
7c659f5
add quick_selenium script for quickly starting up a selenium interact…
mpacer Mar 20, 2018
0994db2
Introduce Notebook class and give usage example in quick_selenium
mpacer Mar 20, 2018
f9dd7c1
add basic cell functionality to Notebook class
mpacer Mar 20, 2018
c9a8504
add body property
mpacer Mar 20, 2018
3d3086e
add edit_cell and execute_cell methods; remove focus_cell edit parameter
mpacer Mar 20, 2018
4b6a0a7
Add CellTypeError and convert_cell_type and wait_for_stale_cell methods
mpacer Mar 20, 2018
2b7f549
Add add_cell and add_markdown_cell methods
mpacer Mar 20, 2018
e2243d6
add method that allows you to close the notebook without confirming
mpacer Mar 22, 2018
86ae162
add first markdown test and a notebook fixture for new notebook
mpacer Mar 22, 2018
bf39dec
move sauce driver logic into isolated function, simplify selenium_driver
mpacer Mar 22, 2018
e0ed2c4
make authenticated browser module scope fixture for permission reasons
mpacer Mar 22, 2018
c220215
use wait_for_selector from utils module in test_dashboard_nav
mpacer Mar 22, 2018
3092800
add wait_for_selector to the cells property and to_command_mode
mpacer Mar 22, 2018
3615d4a
enhance wait_for_selector to handle returning single elements
mpacer Mar 23, 2018
d634f1d
add new_window contextmanager for creating, switching, & waiting on n…
mpacer Mar 23, 2018
a112ab6
add select_kernel function for clicking "new" & selecting a kernel
mpacer Mar 23, 2018
ebef7ba
create new_notebook classmethod creating/switching to new Notebook page
mpacer Mar 23, 2018
5d19785
small changes to naming things
mpacer Mar 23, 2018
3571f16
introduce cell __iter__, & __setitem__/__getitem__ for cells & indices
mpacer Mar 23, 2018
e183fc1
enrich signature for execute_cell to accept both index and cell directly
mpacer Mar 23, 2018
c16dac2
use new rich container __*__ methods to make adding & executing nicer
mpacer Mar 23, 2018
e9971a9
simplify __getitem__ __setitem__ to handle only ints/slices and ints
mpacer Mar 23, 2018
bf4868d
remove wait_for_selector call inside self.cells property
mpacer Mar 23, 2018
02e0ac3
fix add_cell logic, add content param, edit_cell default not render
mpacer Mar 23, 2018
98c09f8
use index method to get cell index in execute_cell
mpacer Mar 23, 2018
5e43458
add append, extend & coerce_to_cell methods
mpacer Mar 23, 2018
d9dd5d8
add run_all method
mpacer Mar 23, 2018
33ca649
add get_rendered_contents helper function
mpacer Mar 23, 2018
0999798
use new utilities, enable more markdown cells to be added easily
mpacer Mar 23, 2018
7808a89
make append actually append to the end of cell list
mpacer Mar 23, 2018
c081af5
add other markdown string conversion examples for test
mpacer Mar 23, 2018
d598ef5
switch fstring to format string
mpacer Mar 23, 2018
3c4596b
Take into account lab as a potential endpoint for the driver
mpacer Mar 23, 2018
79603b4
add quick_notebook utility and give docstring reminder to quit browsers
mpacer Mar 23, 2018
74af79c
Improve docstrings & comments; Remove unused code; Relocate script
mpacer Mar 23, 2018
515f8e2
nicer error and use append directly do not iterate over cells
mpacer Mar 27, 2018
c23ba2a
Docstrings and naming clarity
takluyver Mar 28, 2018
a6f604a
No need for copy
takluyver Mar 28, 2018
7266fd5
Remove unused variant of append for now
takluyver Mar 28, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 36 additions & 20 deletions notebook/tests/selenium/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

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):
Expand All @@ -28,6 +29,7 @@ def _wait_for_server(proc, info_file_path):
time.sleep(0.1)
raise RuntimeError("Didn't find %s in 30 seconds", info_file_path)


@pytest.fixture(scope='session')
def notebook_server():
info = {}
Expand All @@ -50,10 +52,11 @@ def notebook_server():
# 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_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)
Expand All @@ -63,26 +66,38 @@ def notebook_server():
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'):
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)
driver = make_sauce_driver()
elif os.environ.get('JUPYTER_TEST_BROWSER') == 'chrome':
driver = Chrome()
else:
Expand All @@ -93,7 +108,8 @@ def selenium_driver():
# Teardown
driver.quit()

@pytest.fixture

@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))
Expand Down
53 changes: 53 additions & 0 deletions notebook/tests/selenium/quick_selenium.py
Original file line number Diff line number Diff line change
@@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's have a module level docstring to explain that these utilities are not used by the tests, but they're intended for interactive exploration while writing tests.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had intended to add this.


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')
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())
8 changes: 1 addition & 7 deletions notebook/tests/selenium/test_dashboard_nav.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
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


Expand Down Expand Up @@ -40,7 +41,6 @@ def get_list_items(browser):
'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

Expand All @@ -49,12 +49,6 @@ def only_dir_links(browser):
return [i for i in items
if url_in_tree(browser, i['link']) and i['label'] != '..']


def wait_for_selector(browser, selector, timeout=10):
wait = WebDriverWait(browser, timeout)
return wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, selector)))


def test_items(authenticated_browser):
visited_dict = {}
# Going down the tree to collect links
Expand Down
43 changes: 43 additions & 0 deletions notebook/tests/selenium/test_markdown.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import os

import pytest
from selenium.webdriver.common.keys import Keys

from .utils import wait_for_selector, Notebook

pjoin = os.path.join


@pytest.fixture
def notebook(authenticated_browser):
return Notebook.new_notebook(authenticated_browser)


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(notebook):
nb = notebook
cell_text = ["# Foo",
'**Bar**',
'*Baz*',
'```\nx = 1\n```',
'```aaaa\nx = 1\n```',
]
expected_contents = ['<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\n</code></pre>',
'<pre><code class="cm-s-ipython language-aaaa">x = 1\n</code></pre>'
]
nb.append(*cell_text, cell_type="markdown")
nb.run_all()
rendered_contents = get_rendered_contents(nb)
assert rendered_contents == expected_contents
Loading