Skip to content

Commit cdac3f8

Browse files
authored
Add support for Selenium service_log_path path (#1384)
Fixes #1333
1 parent 1bfe52f commit cdac3f8

File tree

9 files changed

+288
-49
lines changed

9 files changed

+288
-49
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
.pydevproject
33
.idea
44
atest/results
5+
utest/output_dir
56
*.pyc
67
*.orig
78
*py.class
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
*** Settings ***
2+
Suite Teardown Close All Browsers
3+
Resource resource.robot
4+
5+
*** Test Cases ***
6+
First Browser With Service Log Path
7+
[Documentation]
8+
... LOG 2:2 INFO STARTS: Browser driver log file created to:
9+
[Setup] OperatingSystem.Remove Files ${OUTPUT DIR}/${BROWSER}.log
10+
Open Browser ${FRONT PAGE} ${BROWSER} service_log_path=${BROWSER}.log
11+
OperatingSystem.File Should Not Be Empty ${OUTPUT DIR}/${BROWSER}.log
12+
13+
Second Browser With Service Log Path And Index
14+
[Setup] OperatingSystem.Remove Files ${OUTPUT DIR}/${BROWSER}-1.log
15+
Open Browser ${FRONT PAGE} ${BROWSER} service_log_path=${BROWSER}-{index}.log
16+
OperatingSystem.File Should Not Be Empty ${OUTPUT DIR}/${BROWSER}-1.log
17+
18+
Third Browser With Service Log Path And Index Should Not Overwrite
19+
[Setup] OperatingSystem.Remove Files ${OUTPUT DIR}/${BROWSER}-2.log
20+
Open Browser ${FRONT PAGE} ${BROWSER} service_log_path=${BROWSER}-{index}.log
21+
OperatingSystem.File Should Not Be Empty ${OUTPUT DIR}/${BROWSER}-2.log
22+
23+
Fourth Browser With Service Log Path In Subfolder
24+
[Setup] OperatingSystem.Remove Files ${OUTPUT DIR}/a_folder/${BROWSER}-1.log
25+
Open Browser ${FRONT PAGE} ${BROWSER} service_log_path=a_folder/${BROWSER}-{index}.log
26+
OperatingSystem.File Should Not Be Empty ${OUTPUT DIR}/a_folder/${BROWSER}-1.log

src/SeleniumLibrary/keywords/browsermanagement.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def close_browser(self):
5858
@keyword
5959
def open_browser(self, url, browser='firefox', alias=None,
6060
remote_url=False, desired_capabilities=None,
61-
ff_profile_dir=None):
61+
ff_profile_dir=None, service_log_path=None):
6262
"""Opens a new browser instance to the given ``url``.
6363
6464
The ``browser`` argument specifies which browser to use, and the
@@ -117,6 +117,15 @@ def open_browser(self, url, browser='firefox', alias=None,
117117
uses. Notice that prior to SeleniumLibrary 3.0, the library
118118
contained its own profile that was used by default.
119119
120+
Optional ``service_log_path`` argument defines the name of the
121+
file where to write the browser driver logs. If the
122+
``service_log_path`` argument contain a marker ``{index}``, it
123+
will be automatically replaced with unique running
124+
index preventing files to be overwritten. Indices start's from 1,
125+
and how they are represented can be customized using Python's
126+
[https://docs.python.org/3/library/string.html#format-string-syntax|
127+
format string syntax].
128+
120129
Examples:
121130
| `Open Browser` | http://example.com | Chrome |
122131
| `Open Browser` | http://example.com | Firefox | alias=Firefox |
@@ -139,6 +148,7 @@ def open_browser(self, url, browser='firefox', alias=None,
139148
new in SeleniumLibrary 3.1.
140149
141150
Using ``alias`` to decide, is the new browser opened is new
151+
in SeleniumLibrary 4.0. Also the ``service_log_path`` is new
142152
in SeleniumLibrary 4.0.
143153
"""
144154
index = self.drivers.get_index(alias)
@@ -148,18 +158,20 @@ def open_browser(self, url, browser='firefox', alias=None,
148158
self.go_to(url)
149159
return index
150160
return self._make_new_browser(url, browser, alias, remote_url,
151-
desired_capabilities, ff_profile_dir)
161+
desired_capabilities, ff_profile_dir,
162+
service_log_path)
152163

153164
def _make_new_browser(self, url, browser='firefox', alias=None,
154165
remote_url=False, desired_capabilities=None,
155-
ff_profile_dir=None):
166+
ff_profile_dir=None, service_log_path=None):
156167
if is_truthy(remote_url):
157168
self.info("Opening browser '%s' to base url '%s' through "
158169
"remote server at '%s'." % (browser, url, remote_url))
159170
else:
160171
self.info("Opening browser '%s' to base url '%s'." % (browser, url))
161172
driver = self._make_driver(browser, desired_capabilities,
162-
ff_profile_dir, remote_url)
173+
ff_profile_dir, remote_url,
174+
service_log_path)
163175
driver = self._wrap_event_firing_webdriver(driver)
164176
try:
165177
driver.get(url)
@@ -489,10 +501,10 @@ def set_browser_implicit_wait(self, value):
489501
self.driver.implicitly_wait(timestr_to_secs(value))
490502

491503
def _make_driver(self, browser, desired_capabilities=None,
492-
profile_dir=None, remote=None):
504+
profile_dir=None, remote=None, service_log_path=None):
493505
driver = WebDriverCreator(self.log_dir).create_driver(
494506
browser=browser, desired_capabilities=desired_capabilities,
495-
remote_url=remote, profile_dir=profile_dir)
507+
remote_url=remote, profile_dir=profile_dir, service_log_path=service_log_path)
496508
driver.set_script_timeout(self.ctx.timeout)
497509
driver.implicitly_wait(self.ctx.implicit_wait)
498510
if self.ctx.speed:

src/SeleniumLibrary/keywords/webdrivertools.py

Lines changed: 65 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1414
# See the License for the specific language governing permissions and
1515
# limitations under the License.
16-
16+
import inspect
1717
import os
1818
import warnings
1919

@@ -50,14 +50,18 @@ def __init__(self, log_dir):
5050
self.log_dir = log_dir
5151

5252
def create_driver(self, browser, desired_capabilities, remote_url,
53-
profile_dir=None):
53+
profile_dir=None, service_log_path=None):
5454
creation_method = self._get_creator_method(browser)
5555
desired_capabilities = self._parse_capabilities(desired_capabilities, browser)
56+
service_log_path = self._get_log_path(service_log_path)
57+
if service_log_path:
58+
logger.info('Browser driver log file created to: %s' % service_log_path)
59+
self._create_directory(service_log_path)
5660
if (creation_method == self.create_firefox
5761
or creation_method == self.create_headless_firefox):
5862
return creation_method(desired_capabilities, remote_url,
59-
profile_dir)
60-
return creation_method(desired_capabilities, remote_url)
63+
profile_dir, service_log_path)
64+
return creation_method(desired_capabilities, remote_url, service_log_path=service_log_path)
6165

6266
def _get_creator_method(self, browser):
6367
browser = browser.lower().replace(' ', '')
@@ -93,29 +97,28 @@ def _remote_capabilities_resolver(self, set_capabilities, default_capabilities):
9397
caps['browserName'] = default_capabilities['browserName']
9498
return {'desired_capabilities': caps}
9599

96-
def create_chrome(self, desired_capabilities, remote_url, options=None):
100+
def create_chrome(self, desired_capabilities, remote_url, options=None, service_log_path=None):
97101
if is_truthy(remote_url):
98102
defaul_caps = webdriver.DesiredCapabilities.CHROME.copy()
99103
desired_capabilities = self._remote_capabilities_resolver(desired_capabilities, defaul_caps)
100104
return self._remote(desired_capabilities, remote_url, options=options)
101-
return webdriver.Chrome(options=options, **desired_capabilities)
105+
return webdriver.Chrome(options=options, service_log_path=service_log_path, **desired_capabilities)
102106

103-
def create_headless_chrome(self, desired_capabilities, remote_url):
107+
def create_headless_chrome(self, desired_capabilities, remote_url, service_log_path=None):
104108
options = webdriver.ChromeOptions()
105109
# Can be changed to options.headless = True when minimum Selenium version is 3.12.0 or greater.
106110
options.set_headless()
107-
return self.create_chrome(desired_capabilities, remote_url, options)
111+
return self.create_chrome(desired_capabilities, remote_url, options, service_log_path)
108112

109-
def create_firefox(self, desired_capabilities, remote_url, ff_profile_dir,
110-
options=None):
113+
def create_firefox(self, desired_capabilities, remote_url, ff_profile_dir, options=None, service_log_path=None):
111114
profile = self._get_ff_profile(ff_profile_dir)
112115
if is_truthy(remote_url):
113116
defaul_caps = webdriver.DesiredCapabilities.FIREFOX.copy()
114117
desired_capabilities = self._remote_capabilities_resolver(desired_capabilities, defaul_caps)
115118
return self._remote(desired_capabilities, remote_url,
116119
profile, options)
117-
desired_capabilities.update(self._geckodriver_log)
118-
return webdriver.Firefox(options=options, firefox_profile=profile,
120+
service_log_path = service_log_path if service_log_path else self._geckodriver_log
121+
return webdriver.Firefox(options=options, firefox_profile=profile, service_log_path=service_log_path,
119122
**desired_capabilities)
120123

121124
def _get_ff_profile(self, ff_profile_dir):
@@ -125,36 +128,47 @@ def _get_ff_profile(self, ff_profile_dir):
125128

126129
@property
127130
def _geckodriver_log(self):
128-
return {'log_path': os.path.join(self.log_dir, 'geckodriver.log')}
131+
log_file = self._get_log_path(os.path.join(self.log_dir, 'geckodriver-{index}.log'))
132+
logger.info('Firefox driver log is always forced to to: %s' % log_file)
133+
return log_file
129134

130135
def create_headless_firefox(self, desired_capabilities, remote_url,
131-
ff_profile_dir):
136+
ff_profile_dir, service_log_path=None):
132137
options = webdriver.FirefoxOptions()
133138
# Can be changed to options.headless = True when minimum Selenium version is 3.12.0 or greater.
134139
options.set_headless()
135-
return self.create_firefox(desired_capabilities, remote_url,
136-
ff_profile_dir, options)
140+
return self.create_firefox(desired_capabilities, remote_url, ff_profile_dir, options, service_log_path)
137141

138-
def create_ie(self, desired_capabilities, remote_url):
142+
def create_ie(self, desired_capabilities, remote_url, service_log_path=None):
139143
if is_truthy(remote_url):
140144
defaul_caps = webdriver.DesiredCapabilities.INTERNETEXPLORER.copy()
141145
desired_capabilities = self._remote_capabilities_resolver(desired_capabilities, defaul_caps)
142146
return self._remote(desired_capabilities, remote_url)
147+
if self._has_service_log_path(webdriver.Ie):
148+
return webdriver.Ie(service_log_path=service_log_path, **desired_capabilities)
149+
logger.warn('This version of Selenium does not support service_log_path argument.')
143150
return webdriver.Ie(**desired_capabilities)
144151

145-
def create_edge(self, desired_capabilities, remote_url):
152+
def _has_service_log_path(self, web_driver):
153+
signature = inspect.getargspec(web_driver.__init__)
154+
return True if 'service_log_path' in signature.args else False
155+
156+
def create_edge(self, desired_capabilities, remote_url, service_log_path=None):
146157
if is_truthy(remote_url):
147158
defaul_caps = webdriver.DesiredCapabilities.EDGE.copy()
148159
desired_capabilities = self._remote_capabilities_resolver(desired_capabilities, defaul_caps)
149160
return self._remote(desired_capabilities, remote_url)
161+
if self._has_service_log_path(webdriver.Ie):
162+
return webdriver.Edge(service_log_path=service_log_path, **desired_capabilities)
163+
logger.warn('This version of Selenium does not support service_log_path argument.')
150164
return webdriver.Edge(**desired_capabilities)
151165

152-
def create_opera(self, desired_capabilities, remote_url):
166+
def create_opera(self, desired_capabilities, remote_url, service_log_path=None):
153167
if is_truthy(remote_url):
154168
defaul_caps = webdriver.DesiredCapabilities.OPERA.copy()
155169
desired_capabilities = self._remote_capabilities_resolver(desired_capabilities, defaul_caps)
156170
return self._remote(desired_capabilities, remote_url)
157-
return webdriver.Opera(**desired_capabilities)
171+
return webdriver.Opera(service_log_path=service_log_path, **desired_capabilities)
158172

159173
def create_safari(self, desired_capabilities, remote_url):
160174
if is_truthy(remote_url):
@@ -163,31 +177,39 @@ def create_safari(self, desired_capabilities, remote_url):
163177
return self._remote(desired_capabilities, remote_url)
164178
return webdriver.Safari(**desired_capabilities)
165179

166-
def create_phantomjs(self, desired_capabilities, remote_url):
180+
def create_phantomjs(self, desired_capabilities, remote_url, service_log_path=None):
167181
warnings.warn('SeleniumLibrary support for PhantomJS has been deprecated, '
168182
'please use headlesschrome or headlessfirefox instead.')
169183
if is_truthy(remote_url):
170184
defaul_caps = webdriver.DesiredCapabilities.PHANTOMJS.copy()
171185
desired_capabilities = self._remote_capabilities_resolver(desired_capabilities, defaul_caps)
172186
return self._remote(desired_capabilities, remote_url)
173-
return webdriver.PhantomJS(**desired_capabilities)
187+
return webdriver.PhantomJS(service_log_path=service_log_path, **desired_capabilities)
174188

175-
def create_htmlunit(self, desired_capabilities, remote_url):
189+
def create_htmlunit(self, desired_capabilities, remote_url, service_log_path=None):
190+
if service_log_path:
191+
logger.warn('Htmlunit does not support service_log_path argument.')
176192
defaul_caps = webdriver.DesiredCapabilities.HTMLUNIT.copy()
177193
desired_capabilities = self._remote_capabilities_resolver(desired_capabilities, defaul_caps)
178194
return self._remote(desired_capabilities, remote_url)
179195

180-
def create_htmlunit_with_js(self, desired_capabilities, remote_url):
196+
def create_htmlunit_with_js(self, desired_capabilities, remote_url, service_log_path=None):
197+
if service_log_path:
198+
logger.warn('Htmlunit does not support service_log_path argument.')
181199
defaul_caps = webdriver.DesiredCapabilities.HTMLUNITWITHJS.copy()
182200
desired_capabilities = self._remote_capabilities_resolver(desired_capabilities, defaul_caps)
183201
return self._remote(desired_capabilities, remote_url)
184202

185-
def create_android(self, desired_capabilities, remote_url):
203+
def create_android(self, desired_capabilities, remote_url, service_log_path=None):
204+
if service_log_path:
205+
logger.warn('Android does not support service_log_path argument.')
186206
defaul_caps = webdriver.DesiredCapabilities.ANDROID.copy()
187207
desired_capabilities = self._remote_capabilities_resolver(desired_capabilities, defaul_caps)
188208
return self._remote(desired_capabilities, remote_url)
189209

190-
def create_iphone(self, desired_capabilities, remote_url):
210+
def create_iphone(self, desired_capabilities, remote_url, service_log_path=None):
211+
if service_log_path:
212+
logger.warn('iPhone does not support service_log_path argument.')
191213
defaul_caps = webdriver.DesiredCapabilities.IPHONE.copy()
192214
desired_capabilities = self._remote_capabilities_resolver(desired_capabilities, defaul_caps)
193215
return self._remote(desired_capabilities, remote_url)
@@ -199,6 +221,23 @@ def _remote(self, desired_capabilities, remote_url,
199221
browser_profile=profile_dir, options=options,
200222
**desired_capabilities)
201223

224+
def _get_log_path(self, log_file):
225+
if is_noney(log_file):
226+
return None
227+
index = 1
228+
while True:
229+
formatted = log_file.format(index=index)
230+
path = os.path.join(self.log_dir, formatted)
231+
# filename didn't contain {index} or unique path was found
232+
if formatted == log_file or not os.path.exists(path):
233+
return path
234+
index += 1
235+
236+
def _create_directory(self, path):
237+
target_dir = os.path.dirname(path)
238+
if not os.path.exists(target_dir):
239+
os.makedirs(target_dir)
240+
202241

203242
class WebDriverCache(ConnectionCache):
204243

utest/run.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env python
22

33
import os
4+
import shutil
45
import sys
56
from os.path import abspath, dirname, join
67
from unittest import defaultTestLoader, TextTestRunner
@@ -9,6 +10,13 @@
910
CURDIR = dirname(abspath(__file__))
1011

1112

13+
def remove_output_dir():
14+
output_dir = os.path.join(CURDIR, 'output_dir')
15+
if os.path.exists(output_dir):
16+
shutil.rmtree(output_dir)
17+
os.mkdir(output_dir)
18+
19+
1220
def run_unit_tests():
1321
sys.path.insert(0, join(CURDIR, os.pardir, 'src'))
1422
try:
@@ -20,4 +28,5 @@ def run_unit_tests():
2028

2129

2230
if __name__ == '__main__':
31+
remove_output_dir()
2332
sys.exit(run_unit_tests())

utest/test/keywords/test_browsermanagement.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def test_open_browser_speed(self):
8989
ctx.event_firing_webdriver = None
9090
ctx.speed = 5.0
9191
browser = mock()
92-
when(webdriver).Chrome(options=None).thenReturn(browser)
92+
when(webdriver).Chrome(options=None, service_log_path=None).thenReturn(browser)
9393
bm = BrowserManagementKeywords(ctx)
9494
bm.open_browser('http://robotframework.org/', 'chrome')
9595
self.assertEqual(browser._speed, 5.0)
@@ -101,7 +101,7 @@ def test_create_webdriver_speed(self):
101101
ctx.event_firing_webdriver = None
102102
ctx.speed = 0.0
103103
browser = mock()
104-
when(webdriver).Chrome(options=None).thenReturn(browser)
104+
when(webdriver).Chrome(options=None, service_log_path=None).thenReturn(browser)
105105
bm = BrowserManagementKeywords(ctx)
106106
bm.open_browser('http://robotframework.org/', 'chrome')
107107
verify(browser, times=0).__call__('_speed')

utest/test/keywords/test_keyword_arguments_browsermanagement.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ def test_open_browser(self):
2222
remote_url = '"http://localhost:4444/wd/hub"'
2323
browser = mock()
2424
when(self.brorser)._make_driver('firefox', None,
25-
None, False).thenReturn(browser)
25+
None, False, None).thenReturn(browser)
2626
alias = self.brorser.open_browser(url)
2727
self.assertEqual(alias, None)
2828

2929
when(self.brorser)._make_driver('firefox', None,
30-
None, remote_url).thenReturn(browser)
30+
None, remote_url, None).thenReturn(browser)
3131
alias = self.brorser.open_browser(url, alias='None',
3232
remote_url=remote_url)
3333
self.assertEqual(alias, None)

0 commit comments

Comments
 (0)