Skip to content

Commit 4ab846e

Browse files
authored
Merge pull request #556 from seleniumbase/improve-safari-automation
Improve automation on Safari browsers
2 parents ed68e3e + 448f5cb commit 4ab846e

File tree

7 files changed

+93
-30
lines changed

7 files changed

+93
-30
lines changed

examples/offline_examples/demo_page.html

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -184,18 +184,30 @@
184184
border-width: 1px;
185185
}
186186
iframe.frameClass1 {
187-
height: 25px;
187+
height: 26px;
188188
width: 32px;
189-
padding: 3px 3px 3px 3px;
189+
padding: 2px 2px 2px 2px;
190190
color: #0C8CDF;
191191
overflow:hidden;
192192
background-color: #fefefe;
193193
border: 2px solid #c1c1c1;
194194
}
195195
iframe.frameClass2 {
196-
height: 25px;
196+
height: 26px;
197197
width: 103px;
198-
padding: 3px 3px 3px 3px;
198+
padding: 2px 2px 2px 2px;
199+
border: none;
200+
margin: none;
201+
color: #0C8CDF;
202+
overflow: hidden;
203+
position: absolute;
204+
background-color: #fefefe;
205+
border: 2px solid #c1c1c1;
206+
}
207+
iframe.frameClass3 {
208+
height: 28px;
209+
width: 34px;
210+
padding: 1px 1px 1px 1px;
199211
border: none;
200212
margin: none;
201213
color: #0C8CDF;
@@ -344,13 +356,14 @@ <h3>Automation Practice</h3>
344356
<td><meter id="meterBar" value="0.25" style="width: 94%;"></meter></td>
345357
</tr>
346358
<tr>
347-
<td><div style="color: #866042;">Image in iFrame:</div></td>
359+
<td><div style="color: #845342;">Image in iFrame:</div></td>
348360
<td style="padding: 4px 4px 3px 4px;">
349361
<iframe id="myFrame1" name="frameName1" class="frameClass1" scrolling="no"
350362
src='data:image/gif;base64,R0lGODlhEAAOALMAAOazToeHh0tLS/7LZv/0jvb29t/f3//Ub//ge8WSLf/rhf/3kdbW1mxsbP//mf///yH5BAAAAAAALAAAAAAQAA4AAARe8L1Ekyky67QZ1hLnjM5UUde0ECwLJoExKcppV0aCcGCmTIHEIUEqjgaORCMxIC6e0CcguWw6aFjsVMkkIr7g77ZKPJjPZqIyd7sJAgVGoEGv2xsBxqNgYPj/gAwXEQA7'></iframe>
351363
&nbsp;&nbsp;
352364
<iframe id="myFrame2" name="frameName2" class="frameClass2" scrolling="no"
353-
src="data:text/html,<h4>Frame%20Text<%2Fh4>"></iframe>
365+
src="data:text/html,<body%20style=%22background-color:%23F2F6F8;%22>
366+
<h4>iFrame%20Text<%2Fh4></body>"></iframe>
354367
</td>
355368
<td>RadioButton 1:<input type="radio" checked id="radioButton1"
356369
name="radioGroup1" class="radioClass1"
@@ -381,12 +394,17 @@ <h3>Automation Practice</h3>
381394
<input type="checkbox" id="checkBox5"
382395
name="checkBoxName5" class="checkBoxClassC" checked />
383396
</td>
384-
<td>
385-
<div style="color: gray;">
386-
Disabled CheckBox:
387-
<input type="checkbox" id="checkBox6"
388-
name="checkBoxName6" class="checkBoxClassD" disabled />
389-
</div>
397+
<td style="color: #845342; padding: 3px 3px 7px 9px; height: 38px;">
398+
CheckBox in iFrame:&nbsp;&nbsp;
399+
<iframe style="padding: 0px 0px 0px 0px;"
400+
id="myFrame3" name="frameName3" class="frameClass3" scrolling="no"
401+
src="data:text/html,
402+
<body%20style=%22background-color:%23F2F6F8;%22>
403+
<input%20type=%22checkbox%22%20id=%22checkBox6%22
404+
%20style=%22padding:%201px%201px%201px%201px;%22
405+
%20name=%22checkBoxName6%22
406+
%20class=%22checkBoxClassD%20fBox%22%20/>
407+
</body>"></iframe>
390408
</td>
391409
</tr>
392410
<tr>

examples/offline_examples/test_demo_page.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ def test_demo_page(self):
5050
self.switch_to_default_content()
5151

5252
# Assert text located inside an iFrame
53-
self.assert_false(self.is_text_visible("Frame Text"))
53+
self.assert_false(self.is_text_visible("iFrame Text"))
5454
self.switch_to_frame("#myFrame2")
55-
self.assert_true(self.is_text_visible("Frame Text"))
55+
self.assert_true(self.is_text_visible("iFrame Text"))
5656
self.switch_to_default_content()
5757

5858
# Verify that clicking a radio button selects it
@@ -74,6 +74,15 @@ def test_demo_page(self):
7474
self.assert_true(self.is_selected("#checkBox3"))
7575
self.assert_true(self.is_selected("#checkBox4"))
7676

77+
# Verify that clicking an iFrame checkbox selects it
78+
self.assert_false(self.is_element_visible(".fBox"))
79+
self.switch_to_frame("#myFrame3")
80+
self.assert_true(self.is_element_visible(".fBox"))
81+
self.assert_false(self.is_selected(".fBox"))
82+
self.click(".fBox")
83+
self.assert_true(self.is_selected(".fBox"))
84+
self.switch_to_default_content()
85+
7786
# Assert link text - Use click_link_text() to click
7887
self.assert_link_text("seleniumbase.com")
7988
self.assert_link_text("SeleniumBase on GitHub")

examples/test_demo_site.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ def test_demo_site(self):
4747
self.switch_to_default_content()
4848

4949
# Assert text located inside an iFrame
50-
self.assert_false(self.is_text_visible("Frame Text"))
50+
self.assert_false(self.is_text_visible("iFrame Text"))
5151
self.switch_to_frame("#myFrame2")
52-
self.assert_true(self.is_text_visible("Frame Text"))
52+
self.assert_true(self.is_text_visible("iFrame Text"))
5353
self.switch_to_default_content()
5454

5555
# Verify that clicking a radio button selects it
@@ -71,6 +71,15 @@ def test_demo_site(self):
7171
self.assert_true(self.is_selected("#checkBox3"))
7272
self.assert_true(self.is_selected("#checkBox4"))
7373

74+
# Verify that clicking an iFrame checkbox selects it
75+
self.assert_false(self.is_element_visible(".fBox"))
76+
self.switch_to_frame("#myFrame3")
77+
self.assert_true(self.is_element_visible(".fBox"))
78+
self.assert_false(self.is_selected(".fBox"))
79+
self.click(".fBox")
80+
self.assert_true(self.is_selected(".fBox"))
81+
self.switch_to_default_content()
82+
7483
# Assert link text - Use click_link_text() to click
7584
self.assert_link_text("seleniumbase.com")
7685
self.assert_link_text("SeleniumBase on GitHub")

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
pip>=20.0.2
1+
pip>=20.1
22
setuptools>=44.1.0;python_version<"3.5"
33
setuptools>=46.1.3;python_version>="3.5"
44
setuptools-scm>=3.5.0

seleniumbase/fixtures/base_case.py

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ def click(self, selector, by=By.CSS_SELECTOR, timeout=None, delay=0):
146146
if self.browser == 'ie' and by == By.LINK_TEXT:
147147
# An issue with clicking Link Text on IE means using jquery
148148
self.__jquery_click(selector, by=by)
149+
elif self.browser == "safari":
150+
if by == By.LINK_TEXT:
151+
self.__jquery_click(selector, by=by)
152+
else:
153+
self.__js_click(selector, by=by)
149154
else:
150155
# Normal click
151156
element.click()
@@ -594,6 +599,9 @@ def click_link_text(self, link_text, timeout=None):
594599
return
595600
self.open(self.__get_href_from_link_text(link_text))
596601
return
602+
if self.browser == "safari":
603+
self.__jquery_click(link_text, by=By.LINK_TEXT)
604+
return
597605
if not self.is_link_text_present(link_text):
598606
self.wait_for_link_text_present(link_text, timeout=timeout)
599607
pre_action_url = self.get_current_url()
@@ -1000,6 +1008,29 @@ def click_visible_elements(self, selector, by=By.CSS_SELECTOR, limit=0):
10001008
self.wait_for_element_present(
10011009
selector, by=by, timeout=settings.SMALL_TIMEOUT)
10021010
elements = self.find_elements(selector, by=by)
1011+
if self.browser == "safari":
1012+
if not limit:
1013+
limit = 0
1014+
num_elements = len(elements)
1015+
if num_elements == 0:
1016+
raise Exception(
1017+
"No matching elements found for selector {%s}!" % selector)
1018+
elif num_elements < limit or limit == 0:
1019+
limit = num_elements
1020+
selector, by = self.__recalculate_selector(selector, by)
1021+
css_selector = self.convert_to_css_selector(selector, by=by)
1022+
last_css_chunk = css_selector.split(' ')[-1]
1023+
if ":" in last_css_chunk:
1024+
self.__js_click_all(css_selector)
1025+
self.wait_for_ready_state_complete()
1026+
return
1027+
else:
1028+
for i in range(1, limit+1):
1029+
new_selector = css_selector + ":nth-of-type(%s)" % str(i)
1030+
if self.is_element_visible(new_selector):
1031+
self.__js_click(new_selector)
1032+
self.wait_for_ready_state_complete()
1033+
return
10031034
click_count = 0
10041035
for element in elements:
10051036
if limit and limit > 0 and click_count >= limit:
@@ -1206,6 +1237,9 @@ def hover_and_click(self, hover_selector, click_selector,
12061237
outdated_driver = False
12071238
element = None
12081239
try:
1240+
if self.browser == "safari":
1241+
# Use the workaround for hover-clicking on Safari
1242+
raise Exception("This Exception will be caught.")
12091243
page_actions.hover_element(self.driver, dropdown_element)
12101244
except Exception:
12111245
outdated_driver = True
@@ -2150,6 +2184,7 @@ def js_click(self, selector, by=By.CSS_SELECTOR, all_matches=False):
21502184
self.__js_click(selector, by=by) # The real "magic" happens
21512185
else:
21522186
self.__js_click_all(selector, by=by) # The real "magic" happens
2187+
self.wait_for_ready_state_complete()
21532188
self.__demo_mode_pause_if_active()
21542189

21552190
def js_click_all(self, selector, by=By.CSS_SELECTOR):
@@ -4233,8 +4268,6 @@ def __jquery_click(self, selector, by=By.CSS_SELECTOR):
42334268
selector, by = self.__recalculate_selector(selector, by)
42344269
self.wait_for_element_present(
42354270
selector, by=by, timeout=settings.SMALL_TIMEOUT)
4236-
if self.is_element_visible(selector, by=by):
4237-
self.__demo_mode_highlight_if_active(selector, by)
42384271
selector = self.convert_to_css_selector(selector, by=by)
42394272
selector = self.__make_css_match_first_element_only(selector)
42404273
click_script = """jQuery('%s')[0].click()""" % selector
@@ -4365,12 +4398,6 @@ def __recalculate_selector(self, selector, by):
43654398
name = page_utils.get_name_from_selector(selector)
43664399
selector = '[name="%s"]' % name
43674400
by = By.CSS_SELECTOR
4368-
if by == By.LINK_TEXT or by == By.PARTIAL_LINK_TEXT:
4369-
if self.browser == "safari" and selector.lower() != selector:
4370-
selector = ("""//a[contains(translate(.,"ABCDEFGHIJKLMNOPQR"""
4371-
"""STUVWXYZ","abcdefghijklmnopqrstuvw"""
4372-
"""xyz"),"%s")]""" % selector.lower())
4373-
by = By.XPATH
43744401
return (selector, by)
43754402

43764403
def __make_css_match_first_element_only(self, selector):

seleniumbase/fixtures/page_actions.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,9 @@ def hover_and_click(driver, hover_selector, click_selector,
131131
stop_ms = start_ms + (timeout * 1000.0)
132132
element = driver.find_element(by=hover_by, value=hover_selector)
133133
hover = ActionChains(driver).move_to_element(element)
134-
hover.perform()
135134
for x in range(int(timeout * 10)):
136135
try:
136+
hover.perform()
137137
element = driver.find_element(by=click_by, value=click_selector)
138138
element.click()
139139
return element
@@ -159,9 +159,9 @@ def hover_element_and_click(driver, element, click_selector,
159159
start_ms = time.time() * 1000.0
160160
stop_ms = start_ms + (timeout * 1000.0)
161161
hover = ActionChains(driver).move_to_element(element)
162-
hover.perform()
163162
for x in range(int(timeout * 10)):
164163
try:
164+
hover.perform()
165165
element = driver.find_element(by=click_by, value=click_selector)
166166
element.click()
167167
return element
@@ -184,9 +184,9 @@ def hover_element_and_double_click(driver, element, click_selector,
184184
start_ms = time.time() * 1000.0
185185
stop_ms = start_ms + (timeout * 1000.0)
186186
hover = ActionChains(driver).move_to_element(element)
187-
hover.perform()
188187
for x in range(int(timeout * 10)):
189188
try:
189+
hover.perform()
190190
element_2 = driver.find_element(by=click_by, value=click_selector)
191191
actions = ActionChains(driver)
192192
actions.move_to_element(element_2)

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545

4646
setup(
4747
name='seleniumbase',
48-
version='1.37.11',
48+
version='1.37.12',
4949
description='Fast, Easy, and Reliable Browser Automation & Testing.',
5050
long_description=long_description,
5151
long_description_content_type='text/markdown',
@@ -81,7 +81,7 @@
8181
"Programming Language :: Python :: 3.8",
8282
],
8383
install_requires=[
84-
'pip>=20.0.2',
84+
'pip>=20.1',
8585
'setuptools',
8686
'setuptools-scm',
8787
'wheel',

0 commit comments

Comments
 (0)