Skip to content

Commit c9d3277

Browse files
author
Brandon Duffany
committed
Get Chrome working with SauceLabs
1 parent 396d648 commit c9d3277

File tree

6 files changed

+110
-25
lines changed

6 files changed

+110
-25
lines changed

codebender_testing/capabilities.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# This file contains a list of capabilities which will be used to instantiate
2+
# the remote selenium webdrivers.
3+
# Each list entry will cause the entire test suite to be run for a remote
4+
# webdriver with the capabilities specified in the entry.
5+
6+
# See here for more on test configuration:
7+
# https://docs.saucelabs.com/reference/test-configuration
8+
9+
- browserName: "firefox"
10+
- browserName: "chrome"
11+
version: 41

codebender_testing/config.py

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import os
22

33
from selenium import webdriver
4+
from selenium.webdriver import chrome
45
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
6+
import yaml
7+
58

69
def _rel_path(*args):
710
"""Forms a path relative to this file's directory."""
@@ -33,6 +36,14 @@ def _rel_path(*args):
3336

3437
_EXTENSIONS_DIR = _rel_path('..', 'extensions')
3538
_FIREFOX_EXTENSION_FNAME = 'codebender.xpi'
39+
_CHROME_EXTENSION_FNAME = 'codebendercc-extension.crx'
40+
41+
# Maximum version number that we can use the Chrome extension with.
42+
# For versions higher than this, we need to use the newer Codebender app
43+
CHROME_EXT_MAX_CHROME_VERSION = 41
44+
45+
# Path to YAML file specifying capability list
46+
DEFAULT_CAPABILITIES_FILE_PATH = _rel_path('capabilities.yaml')
3647

3748
# Files used for testing
3849
TEST_DATA_DIR = _rel_path('..', 'test_data')
@@ -55,22 +66,19 @@ def _get_firefox_profile():
5566
)
5667
return firefox_profile
5768

58-
59-
def _get_chrome_profile():
60-
# TODO
61-
return None
62-
63-
64-
def get_browsers(config_path=None):
69+
def get_browsers(capabilities_file_path=None):
6570
"""Returns a list of capabilities. Each item in the list will cause
6671
the entire suite of tests to be re-run for a browser with those
67-
particular capabilities."""
68-
# TODO: read from browsers.yaml, and allow specifying custom config file.
69-
return [
70-
{
71-
"browserName": "firefox", # OK with other defaults for now.
72-
}
73-
]
72+
particular capabilities.
73+
74+
`capabilities_file_path` is a path to a YAML file specifying a list of
75+
capabilities for each browser. "Capabilities" are the dictionaries
76+
passed as the `desired_capabilities` argument to the webdriver constructor.
77+
"""
78+
if capabilities_file_path is None:
79+
capabilities_file_path = DEFAULT_CAPABILITIES_FILE_PATH
80+
stream = file(capabilities_file_path, 'rb')
81+
return yaml.load(stream)
7482

7583

7684
def create_webdriver(command_executor, desired_capabilities):
@@ -86,10 +94,30 @@ def create_webdriver(command_executor, desired_capabilities):
8694
# Fill in defaults from DesiredCapabilities.{CHROME,FIREFOX} if they are
8795
# missing from the desired_capabilities dict above.
8896
_capabilities = desired_capabilities
97+
browser_profile = None
98+
8999
if browser_name == "chrome":
90100
desired_capabilities = DesiredCapabilities.CHROME.copy()
91101
desired_capabilities.update(_capabilities)
92-
browser_profile = _get_chrome_profile()
102+
103+
# NOTE: the following logic is disabled since the remote webdriver is
104+
# not properly installing the codebender extension. It is kept for
105+
# reference until we can figure out how to properly add the Chrome
106+
# extension.
107+
108+
# # Add chrome extension to capabilities
109+
# options = chrome.options.Options()
110+
# options.add_extension(os.path.join(_EXTENSIONS_DIR, _CHROME_EXTENSION_FNAME))
111+
# desired_capabilities.update(options.to_capabilities())
112+
# # Right now we only support up to v41 for this testing suite.
113+
# if "version" in desired_capabilities:
114+
# if desired_capabilities["version"] > CHROME_EXT_MAX_CHROME_VERSION:
115+
# raise ValueError("The testing suite only supports Chrome versions up to v%d, "
116+
# "but v%d was specified. Please specify a lower version "
117+
# "number." % (CHROME_EXT_MAX_CHROME_VERSION, desired_capabilities["version"]))
118+
# else:
119+
# desired_capabilities["version"] = CHROME_EXT_MAX_CHROME_VERSION
120+
93121
elif browser_name == "firefox":
94122
desired_capabilities = DesiredCapabilities.FIREFOX.copy()
95123
desired_capabilities.update(_capabilities)

codebender_testing/utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,8 @@ def delete_project(self, project_name):
239239
"""Deletes the project specified by `project_name`. Note that this will
240240
navigate to the user's homepage."""
241241
self.open('/')
242+
# Scroll to the bottom so that the footer doesn't obscure anything
243+
self.execute_script("window.scrollTo(0, document.body.scrollHeight);")
242244
created_project = self.get_element(By.LINK_TEXT, project_name)
243245
delete_button_li = created_project.find_element_by_xpath('..')
244246
delete_button = delete_button_li.find_element_by_css_selector('button:last-child')

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
flake8
22
pytest==2.6.4
3+
pyyaml
34
selenium==2.44.0

tests/conftest.py

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,43 @@
1717
def pytest_addoption(parser):
1818
"""Adds command line options to the testing suite."""
1919

20-
parser.addoption("--url", action="store", default=config.BASE_URL,
20+
parser.addoption("-U", "--url", action="store", default=config.BASE_URL,
2121
help="URL to use for testing, e.g. http://localhost, http://codebender.cc")
2222

23-
parser.addoption("--full", action="store_true", default=False,
24-
help="Run the complete set of compile tests (a minimal set of tests is run by default).")
23+
parser.addoption("-F", "--full", action="store_true", default=False,
24+
help="Run the complete set of compile tests "
25+
"(a minimal set of tests is run by default).")
2526

26-
parser.addoption("--source", action="store", default=config.SOURCE_BACHELOR,
27+
parser.addoption("-S", "--source", action="store", default=config.SOURCE_BACHELOR,
2728
help="Indicate the source used to generate the repo. "
28-
"By default, we assume `bachelor`.")
29+
"By default, we assume `bachelor`. "
30+
"You can instead use `codebender_cc` for the live site.")
2931

32+
parser.addoption("-C", "--capabilities", action="store",
33+
default=config.DEFAULT_CAPABILITIES_FILE_PATH,
34+
help="Custom path to a YAML file containing a capability list.")
3035

31-
@pytest.fixture(scope="session", params=config.get_browsers())
32-
def webdriver(request):
36+
37+
def pytest_generate_tests(metafunc):
38+
"""Special function used by pytest to configure test generation."""
39+
40+
# Paremetrize the desired_capabilities fixture on each of the capabilities
41+
# objects in the YAML file.
42+
if 'desired_capabilities' in metafunc.fixturenames:
43+
capabilities_path = metafunc.config.option.capabilities
44+
metafunc.parametrize('desired_capabilities',
45+
config.get_browsers(capabilities_path),
46+
scope="session")
47+
48+
49+
@pytest.fixture(scope="session")
50+
def webdriver(request, desired_capabilities):
3351
"""Returns a webdriver that persists across the entire test session,
3452
and registers a finalizer to close the browser once the session is
3553
complete. The entire test session is repeated once per driver.
3654
"""
3755

38-
# TODO: maybe have a different way of specifying this (?)
3956
command_executor = os.environ['CODEBENDER_SELENIUM_HUB_URL']
40-
desired_capabilities = request.param
4157

4258
driver = config.create_webdriver(command_executor, desired_capabilities)
4359

@@ -113,7 +129,7 @@ def test_some_feature():
113129
@pytest.fixture(autouse=True)
114130
def requires_url(request, testing_url):
115131
"""Skips tests that require a certain site URL in order to run properly.
116-
This is strictly more specific than skip_by_source; consider using that
132+
This is strictly more specific than requires_source; consider using that
117133
marker instead.
118134
119135
This functionality should be invoked as a pytest marker, e.g.:
@@ -128,3 +144,26 @@ def test_some_feature():
128144
required_url = request.node.get_marker('requires_url').args[0]
129145
if required_url.rstrip('/') != testing_url.rstrip('/'):
130146
pytest.skip('skipped test that requires --url=%s' % required_url)
147+
148+
149+
@pytest.fixture(autouse=True)
150+
def requires_extension(request, webdriver):
151+
"""Mark that a test requires the codebender extension.
152+
Ideally, this marker would not be necessary. However, it is used so that we
153+
skip tests when running under chrome that require the extension (for now).
154+
This is due to the fact that the chrome driver leaves open the
155+
"confirm extension" dialogue without actually installing it.
156+
157+
This functionality should be invoked as a pytest marker, e.g.:
158+
159+
```
160+
@pytest.mark.requires_extension
161+
def test_some_feature():
162+
...
163+
```
164+
"""
165+
if request.node.get_marker('requires_extension'):
166+
if webdriver.desired_capabilities["browserName"] == "chrome":
167+
pytest.skip("skipped test that requires codebender extension. "
168+
"The current webdriver is Chrome, and the ChromeDriver "
169+
"does not properly install extensions.")

tests/sketch/test_sketch.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,12 @@ def test_boards_dropdown(self):
5050

5151
assert boards_dropdown.first_selected_option.text == TEST_BOARD
5252

53+
@pytest.mark.requires_extension
5354
def test_ports_dropdown(self):
5455
"""Tests that the ports dropdown exists."""
5556
self.get_element(By.ID, "ports")
5657

58+
@pytest.mark.requires_extension
5759
def test_run_with_no_port(self):
5860
"""Makes sure that there is an error when we attempt to run with no
5961
port selected."""
@@ -63,10 +65,12 @@ def test_run_with_no_port(self):
6365
expected_conditions.text_to_be_present_in_element(
6466
(By.ID, "operation_output"), "Please select a valid port or enable the plugin!!"))
6567

68+
@pytest.mark.requires_extension
6669
def test_speeds_dropdown(self):
6770
"""Tests that the speeds dropdown exists."""
6871
self.get_element(By.ID, "baudrates")
6972

73+
@pytest.mark.requires_extension
7074
def test_serial_monitor_disables_fields(self):
7175
"""Tests that opening the serial monitor disables the port and baudrate
7276
fields."""

0 commit comments

Comments
 (0)