Skip to content

Commit f027620

Browse files
authored
Merge pull request #147 from seleniumbase/fix-edge-case
Fix an edge case with clicking a link hidden in a dropdown menu.
2 parents bcda984 + ccd843d commit f027620

File tree

6 files changed

+66
-45
lines changed

6 files changed

+66
-45
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ sudo: false
33
python:
44
- "2.7"
55
addons:
6-
firefox: "56.0"
6+
firefox: latest
77
chrome: stable
88
install:
99
- "pip install --upgrade pip"
@@ -13,7 +13,7 @@ install:
1313
before_script:
1414
- "flake8 seleniumbase/*.py && flake8 seleniumbase/*/*.py && flake8 seleniumbase/*/*/*.py && flake8 seleniumbase/*/*/*/*.py"
1515
- "export DISPLAY=:99.0 && sh -e /etc/init.d/xvfb start"
16-
- "wget https://chromedriver.storage.googleapis.com/2.33/chromedriver_linux64.zip && unzip chromedriver_linux64.zip && sudo cp chromedriver /usr/local/bin/ && sudo chmod +x /usr/local/bin/chromedriver"
16+
- "wget https://chromedriver.storage.googleapis.com/2.35/chromedriver_linux64.zip && unzip chromedriver_linux64.zip && sudo cp chromedriver /usr/local/bin/ && sudo chmod +x /usr/local/bin/chromedriver"
1717
- "wget https://github.com/mozilla/geckodriver/releases/download/v0.19.0/geckodriver-v0.19.0-linux64.tar.gz -O /tmp/geckodriver.tar.gz && tar -C /opt -xzf /tmp/geckodriver.tar.gz && sudo chmod 755 /opt/geckodriver && sudo ln -fs /opt/geckodriver /usr/bin/geckodriver && sudo ln -fs /opt/geckodriver /usr/local/bin/geckodriver"
1818
# - "wget https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2 && tar -xvf ./phantomjs-2.1.1-linux-x86_64.tar.bz2 && export PATH=$PWD/phantomjs-2.1.1-linux-x86_64/bin:$PATH"
1919
script:

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<img src="https://cdn2.hubspot.net/hubfs/100006/images/SeleniumBase_Head.png" title="SeleniumBase" height="50">
1+
<img src="https://cdn2.hubspot.net/hubfs/100006/images/SeleniumBaseText_F.png" title="SeleniumBase" height="52">
22

33
**WebDriver automation simplified by extending Python's unittest framework.**
44

help_docs/method_summary.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ self.click_chain(selectors_list, by=By.CSS_SELECTOR,
1818

1919
self.is_link_text_present(link_text)
2020

21-
self.get_link_text_attribute(link_text, attribute)
21+
self.get_link_attribute(link_text, attribute, hard_fail)
2222

2323
self.wait_for_link_text_present(link_text, timeout=settings.SMALL_TIMEOUT)
2424

seleniumbase/fixtures/base_case.py

Lines changed: 60 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,10 @@ def is_link_text_present(self, link_text):
183183
return True
184184
return False
185185

186-
def get_link_text_attribute(self, link_text, attribute):
186+
def get_link_attribute(self, link_text, attribute, hard_fail=True):
187+
""" Finds a link by link text and then returns the attribute's value.
188+
If the link text or attribute cannot be found, an exception will
189+
get raised if hard_fail is True (otherwise None is returned). """
187190
self.wait_for_ready_state_complete()
188191
source = self.get_page_source()
189192
soup = BeautifulSoup(source, "html.parser")
@@ -193,9 +196,16 @@ def get_link_text_attribute(self, link_text, attribute):
193196
if html_link.has_attr(attribute):
194197
attribute_value = html_link.get(attribute)
195198
return attribute_value
196-
raise Exception(
197-
'Could not parse link from link_text [%s]' % link_text)
198-
raise Exception("Link Text [%s] was not found!" % link_text)
199+
if hard_fail:
200+
raise Exception(
201+
'Unable to find attribute [%s] from link text [%s]!'
202+
% (attribute, link_text))
203+
else:
204+
return None
205+
if hard_fail:
206+
raise Exception("Link text [%s] was not found!" % link_text)
207+
else:
208+
return None
199209

200210
def wait_for_link_text_present(self, link_text,
201211
timeout=settings.SMALL_TIMEOUT):
@@ -243,33 +253,46 @@ def click_link_text(self, link_text, timeout=settings.SMALL_TIMEOUT):
243253
link_text, timeout=timeout)
244254
element.click()
245255
except Exception:
246-
hidden_css = None
247-
try:
248-
href = self._get_href_from_link_text(link_text)
249-
link_css = '[href="%s"]' % href
256+
found_css = False
257+
text_id = self.get_link_attribute(link_text, "id", False)
258+
if text_id:
259+
link_css = '[id="%s"]' % link_text
260+
found_css = True
261+
262+
if not found_css:
263+
href = self._get_href_from_link_text(link_text, False)
264+
if href:
265+
if href.startswith('/') or page_utils.is_valid_url(href):
266+
link_css = '[href="%s"]' % href
267+
found_css = True
268+
269+
if not found_css:
270+
ngclick = self.get_link_attribute(link_text, "ng-click", False)
271+
if ngclick:
272+
link_css = '[ng-click="%s"]' % ngclick
273+
found_css = True
274+
275+
if not found_css:
276+
onclick = self.get_link_attribute(link_text, "onclick", False)
277+
if onclick:
278+
link_css = '[onclick="%s"]' % onclick
279+
found_css = True
280+
281+
success = False
282+
if found_css:
250283
if self.is_element_visible(link_css):
251284
self.click(link_css)
285+
success = True
252286
else:
253-
hidden_css = link_css
254-
raise Exception("Element %s is not clickable!" % link_css)
255-
except Exception:
256-
try:
257-
ng_click = self._get_ng_click_from_link_text(link_text)
258-
link_css = '[ng-click="%s"]' % (ng_click)
259-
if self.is_element_visible(link_css):
260-
self.click(link_css)
261-
else:
262-
if not hidden_css:
263-
hidden_css = link_css
264-
raise Exception(
265-
"Element %s is not clickable!" % link_css)
266-
except Exception:
267-
# The link text is probably hidden under a dropdown menu
268-
if not self._click_dropdown_link_text(link_text,
269-
hidden_css):
270-
element = self.wait_for_link_text_visible(
271-
link_text, timeout=settings.MINI_TIMEOUT)
272-
element.click()
287+
# The link text might be hidden under a dropdown menu
288+
success = self._click_dropdown_link_text(
289+
link_text, link_css)
290+
291+
if not success:
292+
element = self.wait_for_link_text_visible(
293+
link_text, timeout=settings.MINI_TIMEOUT)
294+
element.click()
295+
273296
if settings.WAIT_FOR_RSC_ON_CLICKS:
274297
self.wait_for_ready_state_complete()
275298
if self.demo_mode:
@@ -1513,8 +1536,10 @@ def process_checks(self, print_only=False):
15131536

15141537
############
15151538

1516-
def _get_href_from_link_text(self, link_text):
1517-
href = self.get_link_text_attribute(link_text, "href")
1539+
def _get_href_from_link_text(self, link_text, hard_fail=True):
1540+
href = self.get_link_attribute(link_text, "href", hard_fail)
1541+
if not href:
1542+
return None
15181543
if href.startswith('//'):
15191544
link = "http:" + href
15201545
elif href.startswith('/'):
@@ -1525,16 +1550,12 @@ def _get_href_from_link_text(self, link_text):
15251550
link = href
15261551
return link
15271552

1528-
def _get_ng_click_from_link_text(self, link_text):
1529-
ng_click = self.get_link_text_attribute(link_text, "ng-click")
1530-
return ng_click
1531-
1532-
def _click_dropdown_link_text(self, link_text, hidden_css):
1533-
""" When a link is hidden under a dropdown menu, use this. """
1553+
def _click_dropdown_link_text(self, link_text, link_css):
1554+
""" When a link may be hidden under a dropdown menu, use this. """
15341555
source = self.get_page_source()
15351556
soup = BeautifulSoup(source, "html.parser")
15361557
drop_down_list = soup.select('[class*=dropdown]')
1537-
csstype = hidden_css.split('[')[1].split('=')[0]
1558+
csstype = link_css.split('[')[1].split('=')[0]
15381559
for item in drop_down_list:
15391560
if link_text in item.text.split('\n') and csstype in item.decode():
15401561
dropdown_css = ""
@@ -1547,8 +1568,8 @@ def _click_dropdown_link_text(self, link_text, hidden_css):
15471568
# The same class names might be used for multiple dropdowns
15481569
try:
15491570
page_actions.hover_element_and_click(
1550-
self.driver, dropdown, hidden_css,
1551-
click_by=By.CSS_SELECTOR, timeout=0.2)
1571+
self.driver, dropdown, link_text,
1572+
click_by=By.LINK_TEXT, timeout=0.1)
15521573
return True
15531574
except Exception:
15541575
pass

server_setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
setup(
1010
name='seleniumbase',
11-
version='1.7.1',
11+
version='1.7.2',
1212
description='Web Automation & Testing Framework - http://seleniumbase.com',
1313
long_description='Web Automation and Testing Framework - seleniumbase.com',
1414
platforms='Mac * Windows * Linux * Docker',

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
setup(
1010
name='seleniumbase',
11-
version='1.7.1',
11+
version='1.7.2',
1212
description='Web Automation & Testing Framework - http://seleniumbase.com',
1313
long_description='Web Automation and Testing Framework - seleniumbase.com',
1414
platforms='Mac * Windows * Linux * Docker',

0 commit comments

Comments
 (0)