Skip to content

Commit cfa0c32

Browse files
committed
Add shim.py which shims firefox & selenium driver
This configures selenium with the browser and extension information
1 parent 74763c8 commit cfa0c32

File tree

1 file changed

+180
-0
lines changed

1 file changed

+180
-0
lines changed

test/selenium/shim.py

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
import os
2+
from contextlib import contextmanager
3+
from collections import namedtuple
4+
import subprocess
5+
import time
6+
7+
from selenium import webdriver
8+
from selenium.webdriver import DesiredCapabilities
9+
from selenium.webdriver.chrome.options import Options
10+
11+
firefox_info = {'extension_id': 'https-everywhere-eff@eff.org', 'uuid': 'd56a5b99-51b6-4e83-ab23-796216679614'}
12+
chrome_info = {'extension_id': 'nmleinhehnmmepmdbjddclicgpfhbdjo'}
13+
14+
15+
BROWSER_TYPES = ['chrome', 'firefox']
16+
BROWSER_NAMES = ['google-chrome', 'google-chrome-stable', 'google-chrome-beta', 'firefox']
17+
18+
Specifics = namedtuple('Specifics', ['manager', 'background_url', 'info'])
19+
20+
parse_stdout = lambda res: res.strip().decode('utf-8')
21+
22+
run_shell_command = lambda command: parse_stdout(subprocess.check_output(command))
23+
24+
get_git_root = lambda: run_shell_command(['git', 'rev-parse', '--show-toplevel'])
25+
26+
27+
def unix_which(command, silent=False):
28+
try:
29+
return run_shell_command(['which', command])
30+
except subprocess.CalledProcessError as e:
31+
if silent:
32+
return None
33+
raise e
34+
35+
36+
def get_browser_type(string):
37+
for t in BROWSER_TYPES:
38+
if t in string:
39+
return t
40+
raise ValueError("couldn't get browser type from %s" % string)
41+
42+
43+
def get_browser_name(string):
44+
if ('/' in string) or ('\\' in string): # its a path
45+
return os.path.basename(string)
46+
else: # its a browser type
47+
for bn in BROWSER_NAMES:
48+
if string in bn and unix_which(bn, silent=True):
49+
return os.path.basename(unix_which(bn))
50+
raise ValueError('Could not get browser name from %s' % string)
51+
52+
53+
def build_crx():
54+
'''Builds the .crx file for Chrome and returns the path to it'''
55+
cmd = [os.path.join(get_git_root(), 'makecrx.sh')]
56+
return os.path.join(get_git_root(), run_shell_command(cmd).split()[-1])
57+
58+
59+
def build_xpi():
60+
cmd = [os.path.join(get_git_root(), 'makexpi.sh')]
61+
return os.path.join(get_git_root(), run_shell_command(cmd).split()[-1])
62+
63+
64+
def install_ext_on_ff(driver, extension_path):
65+
'''
66+
Use Selenium's internal API's to manually send a message to geckodriver
67+
to install the extension. We should remove this once the functionality is
68+
included in Selenium. See https://github.com/SeleniumHQ/selenium/issues/4215
69+
'''
70+
command = 'addonInstall'
71+
driver.command_executor._commands[command] = ('POST', '/session/$sessionId/moz/addon/install')
72+
driver.execute(command, params={'path': extension_path, 'temporary': True})
73+
time.sleep(2)
74+
75+
76+
class Shim:
77+
_browser_msg = '''BROWSER should be one of:
78+
* /path/to/a/browser
79+
* a browser executable name so we can find the browser with "which $BROWSER"
80+
* something from BROWSER_TYPES
81+
'''
82+
__doc__ = 'Chooses the correct driver and extension_url based on the BROWSER environment\nvariable. ' + _browser_msg
83+
84+
def __init__(self, chrome_info, firefox_info):
85+
print('Configuring the test run')
86+
self.chrome_info, self.firefox_info = chrome_info, firefox_info
87+
self._specifics = None
88+
browser = os.environ.get('BROWSER')
89+
# get browser_path and broser_type first
90+
if browser is None:
91+
raise ValueError("The BROWSER environment variable is not set. " + self._browser_msg)
92+
elif ("/" in browser) or ("\\" in browser): # path to a browser binary
93+
self.browser_path = browser
94+
self.browser_type = get_browser_type(self.browser_path)
95+
96+
elif unix_which(browser, silent=True): # executable browser name like 'google-chrome-stable'
97+
self.browser_path = unix_which(browser)
98+
self.browser_type = get_browser_type(browser)
99+
100+
elif get_browser_type(browser): # browser type like 'firefox' or 'chrome'
101+
bname = get_browser_name(browser)
102+
self.browser_path = unix_which(bname)
103+
self.browser_type = browser
104+
else:
105+
raise ValueError("could not infer BROWSER from %s" % browser)
106+
107+
self.extension_path = self.get_ext_path()
108+
self._set_specifics()
109+
print('\nUsing browser path: %s \nwith browser type: %s \nand extension path: %s' %
110+
(self.browser_path, self.browser_type, self.extension_path))
111+
self._set_urls(self.base_url)
112+
113+
def _set_specifics(self):
114+
self._specifics = self._specifics or {
115+
'chrome': Specifics(self.chrome_manager,
116+
'chrome-extension://%s/' % self.chrome_info['extension_id'],
117+
self.chrome_info),
118+
'firefox': Specifics(self.firefox_manager,
119+
'moz-extension://%s/' % self.firefox_info['uuid'],
120+
self.firefox_info)}
121+
self.manager, self.base_url, self.info = self._specifics[self.browser_type]
122+
123+
def _set_urls(self, base_url):
124+
self.base_url = base_url
125+
self.bg_url = base_url + "_generated_background_page.html"
126+
self.popup_url = base_url + "popup.html"
127+
self.options_url = base_url + "options.html"
128+
129+
def get_ext_path(self):
130+
if self.browser_type == 'chrome':
131+
return build_crx()
132+
elif self.browser_type == 'firefox':
133+
return build_xpi()
134+
else:
135+
raise ValueError("bad browser getting extension path")
136+
137+
@property
138+
def wants_xvfb(self):
139+
if self.on_travis or bool(int(os.environ.get('ENABLE_XVFB', 0))):
140+
return True
141+
return False
142+
143+
@property
144+
def on_travis(self):
145+
if "TRAVIS" in os.environ:
146+
return True
147+
return False
148+
149+
@contextmanager
150+
def chrome_manager(self):
151+
opts = Options()
152+
if self.on_travis: # github.com/travis-ci/travis-ci/issues/938
153+
opts.add_argument("--no-sandbox")
154+
opts.add_extension(self.extension_path)
155+
opts.binary_location = self.browser_path
156+
opts.add_experimental_option("prefs", {"profile.block_third_party_cookies": False})
157+
158+
caps = DesiredCapabilities.CHROME.copy()
159+
160+
driver = webdriver.Chrome(chrome_options=opts, desired_capabilities=caps)
161+
try:
162+
yield driver
163+
finally:
164+
driver.quit()
165+
166+
@contextmanager
167+
def firefox_manager(self):
168+
ffp = webdriver.FirefoxProfile()
169+
# make extension id constant across runs
170+
ffp.set_preference('extensions.webextensions.uuids', '{"%s": "%s"}' %
171+
(self.info['extension_id'], self.info['uuid']))
172+
173+
driver = webdriver.Firefox(firefox_profile=ffp, firefox_binary=self.browser_path)
174+
install_ext_on_ff(driver, self.extension_path)
175+
try:
176+
yield driver
177+
finally:
178+
time.sleep(2)
179+
driver.quit()
180+
time.sleep(2)

0 commit comments

Comments
 (0)