-
Notifications
You must be signed in to change notification settings - Fork 5k
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
Testing with Selenium & Sauce labs #3321
Changes from all commits
aa9272a
a8c4258
9759074
c417727
162e7aa
f578d4e
7bfb7a8
b2b449f
0c65390
739b14a
dca1255
5391f32
93d9fa6
1eee66e
daeea41
2e8bf3b
a1189fc
c0361cb
cdee97a
abc38a7
b1f9cb2
3be1943
58e7e48
9390b14
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import json | ||
import os | ||
import pytest | ||
import requests | ||
from subprocess import Popen | ||
import sys | ||
from testpath.tempdir import TemporaryDirectory | ||
import time | ||
from urllib.parse import urljoin | ||
|
||
from selenium.webdriver import Firefox, Remote, Chrome | ||
|
||
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 _get_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") | ||
return Remote(desired_capabilities=capabilities, | ||
command_executor="http://%s/wd/hub" % hub_url) | ||
elif os.environ.get('JUPYTER_TEST_BROWSER') == 'chrome': | ||
return Chrome() | ||
else: | ||
return Firefox() | ||
|
||
@pytest.fixture | ||
def browser(notebook_server): | ||
b = _get_selenium_driver() | ||
b.get("{url}?token={token}".format(**notebook_server)) | ||
yield b | ||
b.quit() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
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 | ||
|
||
pjoin = os.path.join | ||
|
||
def get_list_items(browser): | ||
return [{ | ||
'link': a.get_attribute('href'), | ||
'label': a.find_element_by_class_name('item_name').text, | ||
} for a in browser.find_elements_by_class_name('item_link')] | ||
|
||
|
||
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(browser, visited=None): | ||
tree_root_url = browser.current_url | ||
if visited is None: | ||
visited = set() | ||
|
||
wait_for_selector(browser, '.item_link') | ||
items = get_list_items(browser) | ||
print(browser.current_url, len(items)) | ||
for item in items: | ||
print(item) | ||
url = item['link'] | ||
if url.startswith(tree_root_url): | ||
print("Going to", url) | ||
if url in visited: | ||
continue | ||
visited.add(url) | ||
browser.get(url) | ||
wait_for_selector(browser, '.item_link') | ||
assert browser.current_url == url | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've tried to translate the JS test I removed directly to Python to illustrate how the machinery works, but I don't think it's actually a very meaningful test. The next step will be to write some new tests using this; I'm planning to leave that for other PRs, because I think there's already enough to look at in this PR. |
||
|
||
test_items(browser, visited) | ||
#browser.back() | ||
|
||
print() |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes it nice n' easy, this is great.