How to use __slow_mode_pause_if_active method in SeleniumBase

Best Python code snippet using SeleniumBase

base_case.py

Source:base_case.py Github

copy

Full Screen

...180 self.__demo_mode_pause_if_active()181 else:182 self.__demo_mode_pause_if_active(tiny=True)183 elif self.slow_mode:184 self.__slow_mode_pause_if_active()185 def slow_click(self, selector, by=By.CSS_SELECTOR, timeout=None):186 """ Similar to click(), but pauses for a brief moment before clicking.187 When used in combination with setting the user-agent, you can often188 bypass bot-detection by tricking websites into thinking that you're189 not a bot. (Useful on websites that block web automation tools.)190 To set the user-agent, use: ``--agent=AGENT``.191 Here's an example message from GitHub's bot-blocker:192 ``You have triggered an abuse detection mechanism...`` """193 if not timeout:194 timeout = settings.SMALL_TIMEOUT195 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:196 timeout = self.__get_new_timeout(timeout)197 if not self.demo_mode:198 self.click(selector, by=by, timeout=timeout, delay=1.05)199 else:200 # Demo Mode already includes a small delay201 self.click(selector, by=by, timeout=timeout, delay=0.25)202 def double_click(self, selector, by=By.CSS_SELECTOR, timeout=None):203 from selenium.webdriver.common.action_chains import ActionChains204 if not timeout:205 timeout = settings.SMALL_TIMEOUT206 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:207 timeout = self.__get_new_timeout(timeout)208 if page_utils.is_xpath_selector(selector):209 by = By.XPATH210 element = page_actions.wait_for_element_visible(211 self.driver, selector, by, timeout=timeout)212 self.__demo_mode_highlight_if_active(selector, by)213 if not self.demo_mode:214 self.__scroll_to_element(element)215 pre_action_url = self.driver.current_url216 try:217 actions = ActionChains(self.driver)218 actions.move_to_element(element)219 actions.double_click(element)220 actions.perform()221 except (StaleElementReferenceException, ENI_Exception):222 self.wait_for_ready_state_complete()223 time.sleep(0.05)224 element = page_actions.wait_for_element_visible(225 self.driver, selector, by, timeout=timeout)226 actions = ActionChains(self.driver)227 actions.move_to_element(element)228 actions.double_click(element)229 actions.perform()230 if settings.WAIT_FOR_RSC_ON_CLICKS:231 self.wait_for_ready_state_complete()232 if self.demo_mode:233 if self.driver.current_url != pre_action_url:234 self.__demo_mode_pause_if_active()235 else:236 self.__demo_mode_pause_if_active(tiny=True)237 elif self.slow_mode:238 self.__slow_mode_pause_if_active()239 def click_chain(self, selectors_list, by=By.CSS_SELECTOR,240 timeout=None, spacing=0):241 """ This method clicks on a list of elements in succession.242 'spacing' is the amount of time to wait between clicks. (sec) """243 if not timeout:244 timeout = settings.SMALL_TIMEOUT245 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:246 timeout = self.__get_new_timeout(timeout)247 for selector in selectors_list:248 self.click(selector, by=by, timeout=timeout)249 if spacing > 0:250 time.sleep(spacing)251 def type(self, selector, text, by=By.CSS_SELECTOR,252 timeout=None, retry=False):253 """ The short version of update_text(), which clears existing text254 and adds new text into the text field.255 We want to keep the other version for backward compatibility. """256 if not timeout:257 timeout = settings.LARGE_TIMEOUT258 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:259 timeout = self.__get_new_timeout(timeout)260 if page_utils.is_xpath_selector(selector):261 by = By.XPATH262 self.update_text(selector, text, by=by, timeout=timeout, retry=retry)263 def input(self, selector, text, by=By.CSS_SELECTOR,264 timeout=None, retry=False):265 """ Same as update_text(). """266 if not timeout:267 timeout = settings.LARGE_TIMEOUT268 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:269 timeout = self.__get_new_timeout(timeout)270 if page_utils.is_xpath_selector(selector):271 by = By.XPATH272 self.update_text(selector, text, by=by, timeout=timeout, retry=retry)273 def update_text(self, selector, new_value, by=By.CSS_SELECTOR,274 timeout=None, retry=False):275 """ This method updates an element's text field with new text.276 Has multiple parts:277 * Waits for the element to be visible.278 * Waits for the element to be interactive.279 * Clears the text field.280 * Types in the new text.281 * Hits Enter/Submit (if the text ends in "\n").282 @Params283 selector - the selector of the text field284 new_value - the new value to type into the text field285 by - the type of selector to search by (Default: CSS Selector)286 timeout - how long to wait for the selector to be visible287 retry - if True, use JS if the Selenium text update fails288 """289 if not timeout:290 timeout = settings.LARGE_TIMEOUT291 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:292 timeout = self.__get_new_timeout(timeout)293 if page_utils.is_xpath_selector(selector):294 by = By.XPATH295 element = self.wait_for_element_visible(296 selector, by=by, timeout=timeout)297 self.__demo_mode_highlight_if_active(selector, by)298 if not self.demo_mode:299 self.__scroll_to_element(element)300 try:301 element.clear()302 except (StaleElementReferenceException, ENI_Exception):303 self.wait_for_ready_state_complete()304 time.sleep(0.06)305 element = self.wait_for_element_visible(306 selector, by=by, timeout=timeout)307 try:308 element.clear()309 except Exception:310 pass # Clearing the text field first isn't critical311 except Exception:312 pass # Clearing the text field first isn't critical313 self.__demo_mode_pause_if_active(tiny=True)314 pre_action_url = self.driver.current_url315 if type(new_value) is int or type(new_value) is float:316 new_value = str(new_value)317 try:318 if not new_value.endswith('\n'):319 element.send_keys(new_value)320 if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:321 self.wait_for_ready_state_complete()322 else:323 new_value = new_value[:-1]324 element.send_keys(new_value)325 element.send_keys(Keys.RETURN)326 if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:327 self.wait_for_ready_state_complete()328 except (StaleElementReferenceException, ENI_Exception):329 self.wait_for_ready_state_complete()330 time.sleep(0.06)331 element = self.wait_for_element_visible(332 selector, by=by, timeout=timeout)333 element.clear()334 if not new_value.endswith('\n'):335 element.send_keys(new_value)336 else:337 new_value = new_value[:-1]338 element.send_keys(new_value)339 element.send_keys(Keys.RETURN)340 if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:341 self.wait_for_ready_state_complete()342 except Exception:343 exc_message = self.__get_improved_exception_message()344 raise Exception(exc_message)345 if (retry and element.get_attribute('value') != new_value and (346 not new_value.endswith('\n'))):347 logging.debug('update_text() is falling back to JavaScript!')348 self.set_value(selector, new_value, by=by)349 if self.demo_mode:350 if self.driver.current_url != pre_action_url:351 self.__demo_mode_pause_if_active()352 else:353 self.__demo_mode_pause_if_active(tiny=True)354 elif self.slow_mode:355 self.__slow_mode_pause_if_active()356 def add_text(self, selector, text, by=By.CSS_SELECTOR, timeout=None):357 """ The more-reliable version of driver.send_keys()358 Similar to update_text(), but won't clear the text field first. """359 if not timeout:360 timeout = settings.LARGE_TIMEOUT361 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:362 timeout = self.__get_new_timeout(timeout)363 if page_utils.is_xpath_selector(selector):364 by = By.XPATH365 element = self.wait_for_element_visible(366 selector, by=by, timeout=timeout)367 self.__demo_mode_highlight_if_active(selector, by)368 if not self.demo_mode:369 self.__scroll_to_element(element)370 pre_action_url = self.driver.current_url371 try:372 if not text.endswith('\n'):373 element.send_keys(text)374 else:375 text = text[:-1]376 element.send_keys(text)377 element.send_keys(Keys.RETURN)378 if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:379 self.wait_for_ready_state_complete()380 except (StaleElementReferenceException, ENI_Exception):381 self.wait_for_ready_state_complete()382 time.sleep(0.06)383 element = self.wait_for_element_visible(384 selector, by=by, timeout=timeout)385 if not text.endswith('\n'):386 element.send_keys(text)387 else:388 text = text[:-1]389 element.send_keys(text)390 element.send_keys(Keys.RETURN)391 if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:392 self.wait_for_ready_state_complete()393 except Exception:394 exc_message = self.__get_improved_exception_message()395 raise Exception(exc_message)396 if self.demo_mode:397 if self.driver.current_url != pre_action_url:398 self.__demo_mode_pause_if_active()399 else:400 self.__demo_mode_pause_if_active(tiny=True)401 elif self.slow_mode:402 self.__slow_mode_pause_if_active()403 def send_keys(self, selector, text, by=By.CSS_SELECTOR, timeout=None):404 """ Same as add_text() """405 if not timeout:406 timeout = settings.LARGE_TIMEOUT407 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:408 timeout = self.__get_new_timeout(timeout)409 if page_utils.is_xpath_selector(selector):410 by = By.XPATH411 self.add_text(selector, text, by=by, timeout=timeout)412 def submit(self, selector, by=By.CSS_SELECTOR):413 """ Alternative to self.driver.find_element_by_*(SELECTOR).submit() """414 if page_utils.is_xpath_selector(selector):415 by = By.XPATH416 element = self.wait_for_element_visible(417 selector, by=by, timeout=settings.SMALL_TIMEOUT)418 element.submit()419 self.__demo_mode_pause_if_active()420 def refresh_page(self):421 self.__last_page_load_url = None422 self.driver.refresh()423 self.wait_for_ready_state_complete()424 def refresh(self):425 """ The shorter version of self.refresh_page() """426 self.refresh_page()427 def get_current_url(self):428 return self.driver.current_url429 def get_page_source(self):430 self.wait_for_ready_state_complete()431 return self.driver.page_source432 def get_page_title(self):433 self.wait_for_ready_state_complete()434 self.wait_for_element_present("title", timeout=settings.SMALL_TIMEOUT)435 time.sleep(0.03)436 return self.driver.title437 def get_title(self):438 """ The shorter version of self.get_page_title() """439 return self.get_page_title()440 def go_back(self):441 self.__last_page_load_url = None442 self.driver.back()443 if self.browser == "safari":444 self.driver.refresh()445 self.wait_for_ready_state_complete()446 self.__demo_mode_pause_if_active()447 def go_forward(self):448 self.__last_page_load_url = None449 self.driver.forward()450 if self.browser == "safari":451 self.driver.refresh()452 self.wait_for_ready_state_complete()453 self.__demo_mode_pause_if_active()454 def is_element_present(self, selector, by=By.CSS_SELECTOR):455 selector, by = self.__recalculate_selector(selector, by)456 return page_actions.is_element_present(self.driver, selector, by)457 def is_element_visible(self, selector, by=By.CSS_SELECTOR):458 selector, by = self.__recalculate_selector(selector, by)459 return page_actions.is_element_visible(self.driver, selector, by)460 def is_text_visible(self, text, selector="html", by=By.CSS_SELECTOR):461 self.wait_for_ready_state_complete()462 time.sleep(0.01)463 selector, by = self.__recalculate_selector(selector, by)464 return page_actions.is_text_visible(self.driver, text, selector, by)465 def is_link_text_visible(self, link_text):466 self.wait_for_ready_state_complete()467 time.sleep(0.01)468 return page_actions.is_element_visible(self.driver, link_text,469 by=By.LINK_TEXT)470 def is_partial_link_text_visible(self, partial_link_text):471 self.wait_for_ready_state_complete()472 time.sleep(0.01)473 return page_actions.is_element_visible(self.driver, partial_link_text,474 by=By.PARTIAL_LINK_TEXT)475 def is_link_text_present(self, link_text):476 """ Returns True if the link text appears in the HTML of the page.477 The element doesn't need to be visible,478 such as elements hidden inside a dropdown selection. """479 soup = self.get_beautiful_soup()480 html_links = soup.find_all('a')481 for html_link in html_links:482 if html_link.text.strip() == link_text.strip():483 return True484 return False485 def is_partial_link_text_present(self, link_text):486 """ Returns True if the partial link appears in the HTML of the page.487 The element doesn't need to be visible,488 such as elements hidden inside a dropdown selection. """489 soup = self.get_beautiful_soup()490 html_links = soup.find_all('a')491 for html_link in html_links:492 if link_text.strip() in html_link.text.strip():493 return True494 return False495 def get_link_attribute(self, link_text, attribute, hard_fail=True):496 """ Finds a link by link text and then returns the attribute's value.497 If the link text or attribute cannot be found, an exception will498 get raised if hard_fail is True (otherwise None is returned). """499 soup = self.get_beautiful_soup()500 html_links = soup.find_all('a')501 for html_link in html_links:502 if html_link.text.strip() == link_text.strip():503 if html_link.has_attr(attribute):504 attribute_value = html_link.get(attribute)505 return attribute_value506 if hard_fail:507 raise Exception(508 'Unable to find attribute {%s} from link text {%s}!'509 % (attribute, link_text))510 else:511 return None512 if hard_fail:513 raise Exception("Link text {%s} was not found!" % link_text)514 else:515 return None516 def get_link_text_attribute(self, link_text, attribute, hard_fail=True):517 """ Same as self.get_link_attribute()518 Finds a link by link text and then returns the attribute's value.519 If the link text or attribute cannot be found, an exception will520 get raised if hard_fail is True (otherwise None is returned). """521 return self.get_link_attribute(link_text, attribute, hard_fail)522 def get_partial_link_text_attribute(self, link_text, attribute,523 hard_fail=True):524 """ Finds a link by partial link text and then returns the attribute's525 value. If the partial link text or attribute cannot be found, an526 exception will get raised if hard_fail is True (otherwise None527 is returned). """528 soup = self.get_beautiful_soup()529 html_links = soup.find_all('a')530 for html_link in html_links:531 if link_text.strip() in html_link.text.strip():532 if html_link.has_attr(attribute):533 attribute_value = html_link.get(attribute)534 return attribute_value535 if hard_fail:536 raise Exception(537 'Unable to find attribute {%s} from '538 'partial link text {%s}!'539 % (attribute, link_text))540 else:541 return None542 if hard_fail:543 raise Exception(544 "Partial Link text {%s} was not found!" % link_text)545 else:546 return None547 def click_link_text(self, link_text, timeout=None):548 """ This method clicks link text on a page """549 # If using phantomjs, might need to extract and open the link directly550 if not timeout:551 timeout = settings.SMALL_TIMEOUT552 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:553 timeout = self.__get_new_timeout(timeout)554 if self.browser == 'phantomjs':555 if self.is_link_text_visible(link_text):556 element = self.wait_for_link_text_visible(557 link_text, timeout=timeout)558 element.click()559 return560 self.open(self.__get_href_from_link_text(link_text))561 return562 if not self.is_link_text_present(link_text):563 self.wait_for_link_text_present(link_text, timeout=timeout)564 pre_action_url = self.get_current_url()565 try:566 element = self.wait_for_link_text_visible(567 link_text, timeout=0.2)568 self.__demo_mode_highlight_if_active(link_text, by=By.LINK_TEXT)569 try:570 element.click()571 except (StaleElementReferenceException, ENI_Exception):572 self.wait_for_ready_state_complete()573 time.sleep(0.05)574 element = self.wait_for_link_text_visible(575 link_text, timeout=timeout)576 element.click()577 except Exception:578 found_css = False579 text_id = self.get_link_attribute(link_text, "id", False)580 if text_id:581 link_css = '[id="%s"]' % link_text582 found_css = True583 if not found_css:584 href = self.__get_href_from_link_text(link_text, False)585 if href:586 if href.startswith('/') or page_utils.is_valid_url(href):587 link_css = '[href="%s"]' % href588 found_css = True589 if not found_css:590 ngclick = self.get_link_attribute(link_text, "ng-click", False)591 if ngclick:592 link_css = '[ng-click="%s"]' % ngclick593 found_css = True594 if not found_css:595 onclick = self.get_link_attribute(link_text, "onclick", False)596 if onclick:597 link_css = '[onclick="%s"]' % onclick598 found_css = True599 success = False600 if found_css:601 if self.is_element_visible(link_css):602 self.click(link_css)603 success = True604 else:605 # The link text might be hidden under a dropdown menu606 success = self.__click_dropdown_link_text(607 link_text, link_css)608 if not success:609 element = self.wait_for_link_text_visible(610 link_text, timeout=settings.MINI_TIMEOUT)611 element.click()612 if settings.WAIT_FOR_RSC_ON_CLICKS:613 self.wait_for_ready_state_complete()614 if self.demo_mode:615 if self.driver.current_url != pre_action_url:616 self.__demo_mode_pause_if_active()617 else:618 self.__demo_mode_pause_if_active(tiny=True)619 elif self.slow_mode:620 self.__slow_mode_pause_if_active()621 def click_link(self, link_text, timeout=None):622 """ Same as self.click_link_text() """623 if not timeout:624 timeout = settings.SMALL_TIMEOUT625 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:626 timeout = self.__get_new_timeout(timeout)627 self.click_link_text(link_text, timeout=timeout)628 def click_partial_link_text(self, partial_link_text, timeout=None):629 """ This method clicks the partial link text on a page. """630 # If using phantomjs, might need to extract and open the link directly631 if not timeout:632 timeout = settings.SMALL_TIMEOUT633 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:634 timeout = self.__get_new_timeout(timeout)635 if self.browser == 'phantomjs':636 if self.is_partial_link_text_visible(partial_link_text):637 element = self.wait_for_partial_link_text(partial_link_text)638 element.click()639 return640 soup = self.get_beautiful_soup()641 html_links = soup.fetch('a')642 for html_link in html_links:643 if partial_link_text in html_link.text:644 for html_attribute in html_link.attrs:645 if html_attribute[0] == 'href':646 href = html_attribute[1]647 if href.startswith('//'):648 link = "http:" + href649 elif href.startswith('/'):650 url = self.driver.current_url651 domain_url = self.get_domain_url(url)652 link = domain_url + href653 else:654 link = href655 self.open(link)656 return657 raise Exception(658 'Could not parse link from partial link_text '659 '{%s}' % partial_link_text)660 raise Exception(661 "Partial link text {%s} was not found!" % partial_link_text)662 if not self.is_partial_link_text_present(partial_link_text):663 self.wait_for_partial_link_text_present(664 partial_link_text, timeout=timeout)665 pre_action_url = self.get_current_url()666 try:667 element = self.wait_for_partial_link_text(668 partial_link_text, timeout=0.2)669 self.__demo_mode_highlight_if_active(670 partial_link_text, by=By.LINK_TEXT)671 try:672 element.click()673 except (StaleElementReferenceException, ENI_Exception):674 self.wait_for_ready_state_complete()675 time.sleep(0.05)676 element = self.wait_for_partial_link_text(677 partial_link_text, timeout=timeout)678 element.click()679 except Exception:680 found_css = False681 text_id = self.get_partial_link_text_attribute(682 partial_link_text, "id", False)683 if text_id:684 link_css = '[id="%s"]' % partial_link_text685 found_css = True686 if not found_css:687 href = self.__get_href_from_partial_link_text(688 partial_link_text, False)689 if href:690 if href.startswith('/') or page_utils.is_valid_url(href):691 link_css = '[href="%s"]' % href692 found_css = True693 if not found_css:694 ngclick = self.get_partial_link_text_attribute(695 partial_link_text, "ng-click", False)696 if ngclick:697 link_css = '[ng-click="%s"]' % ngclick698 found_css = True699 if not found_css:700 onclick = self.get_partial_link_text_attribute(701 partial_link_text, "onclick", False)702 if onclick:703 link_css = '[onclick="%s"]' % onclick704 found_css = True705 success = False706 if found_css:707 if self.is_element_visible(link_css):708 self.click(link_css)709 success = True710 else:711 # The link text might be hidden under a dropdown menu712 success = self.__click_dropdown_partial_link_text(713 partial_link_text, link_css)714 if not success:715 element = self.wait_for_link_text_visible(716 partial_link_text, timeout=settings.MINI_TIMEOUT)717 element.click()718 if settings.WAIT_FOR_RSC_ON_CLICKS:719 self.wait_for_ready_state_complete()720 if self.demo_mode:721 if self.driver.current_url != pre_action_url:722 self.__demo_mode_pause_if_active()723 else:724 self.__demo_mode_pause_if_active(tiny=True)725 elif self.slow_mode:726 self.__slow_mode_pause_if_active()727 def get_text(self, selector, by=By.CSS_SELECTOR, timeout=None):728 if not timeout:729 timeout = settings.SMALL_TIMEOUT730 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:731 timeout = self.__get_new_timeout(timeout)732 if page_utils.is_xpath_selector(selector):733 by = By.XPATH734 self.wait_for_ready_state_complete()735 time.sleep(0.01)736 element = page_actions.wait_for_element_visible(737 self.driver, selector, by, timeout)738 try:739 element_text = element.text740 except (StaleElementReferenceException, ENI_Exception):741 self.wait_for_ready_state_complete()742 time.sleep(0.06)743 element = page_actions.wait_for_element_visible(744 self.driver, selector, by, timeout)745 element_text = element.text746 return element_text747 def get_attribute(self, selector, attribute, by=By.CSS_SELECTOR,748 timeout=None):749 """ This method uses JavaScript to get the value of an attribute. """750 if not timeout:751 timeout = settings.SMALL_TIMEOUT752 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:753 timeout = self.__get_new_timeout(timeout)754 selector, by = self.__recalculate_selector(selector, by)755 self.wait_for_ready_state_complete()756 time.sleep(0.01)757 element = page_actions.wait_for_element_present(758 self.driver, selector, by, timeout)759 try:760 attribute_value = element.get_attribute(attribute)761 except (StaleElementReferenceException, ENI_Exception):762 self.wait_for_ready_state_complete()763 time.sleep(0.06)764 element = page_actions.wait_for_element_present(765 self.driver, selector, by, timeout)766 attribute_value = element.get_attribute(attribute)767 if attribute_value is not None:768 return attribute_value769 else:770 raise Exception("Element {%s} has no attribute {%s}!" % (771 selector, attribute))772 def set_attribute(self, selector, attribute, value, by=By.CSS_SELECTOR,773 timeout=None):774 """ This method uses JavaScript to set/update an attribute.775 Only the first matching selector from querySelector() is used. """776 if not timeout:777 timeout = settings.SMALL_TIMEOUT778 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:779 timeout = self.__get_new_timeout(timeout)780 selector, by = self.__recalculate_selector(selector, by)781 if self.is_element_visible(selector, by=by):782 try:783 self.scroll_to(selector, by=by, timeout=timeout)784 except Exception:785 pass786 attribute = re.escape(attribute)787 attribute = self.__escape_quotes_if_needed(attribute)788 value = re.escape(value)789 value = self.__escape_quotes_if_needed(value)790 css_selector = self.convert_to_css_selector(selector, by=by)791 css_selector = re.escape(css_selector)792 css_selector = self.__escape_quotes_if_needed(css_selector)793 script = ("""document.querySelector('%s').setAttribute('%s','%s');"""794 % (css_selector, attribute, value))795 self.execute_script(script)796 def set_attributes(self, selector, attribute, value, by=By.CSS_SELECTOR):797 """ This method uses JavaScript to set/update a common attribute.798 All matching selectors from querySelectorAll() are used.799 Example => (Make all links on a website redirect to Google):800 self.set_attributes("a", "href", "https://google.com") """801 selector, by = self.__recalculate_selector(selector, by)802 attribute = re.escape(attribute)803 attribute = self.__escape_quotes_if_needed(attribute)804 value = re.escape(value)805 value = self.__escape_quotes_if_needed(value)806 css_selector = self.convert_to_css_selector(selector, by=by)807 css_selector = re.escape(css_selector)808 css_selector = self.__escape_quotes_if_needed(css_selector)809 script = ("""var $elements = document.querySelectorAll('%s');810 var index = 0, length = $elements.length;811 for(; index < length; index++){812 $elements[index].setAttribute('%s','%s');}"""813 % (css_selector, attribute, value))814 try:815 self.execute_script(script)816 except Exception:817 pass818 def set_attribute_all(self, selector, attribute, value,819 by=By.CSS_SELECTOR):820 """ Same as set_attributes(), but using querySelectorAll naming scheme.821 This method uses JavaScript to set/update a common attribute.822 All matching selectors from querySelectorAll() are used.823 Example => (Make all links on a website redirect to Google):824 self.set_attribute_all("a", "href", "https://google.com") """825 self.set_attributes(selector, attribute, value, by=by)826 def remove_attribute(self, selector, attribute, by=By.CSS_SELECTOR,827 timeout=None):828 """ This method uses JavaScript to remove an attribute.829 Only the first matching selector from querySelector() is used. """830 if not timeout:831 timeout = settings.SMALL_TIMEOUT832 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:833 timeout = self.__get_new_timeout(timeout)834 selector, by = self.__recalculate_selector(selector, by)835 if self.is_element_visible(selector, by=by):836 try:837 self.scroll_to(selector, by=by, timeout=timeout)838 except Exception:839 pass840 attribute = re.escape(attribute)841 attribute = self.__escape_quotes_if_needed(attribute)842 css_selector = self.convert_to_css_selector(selector, by=by)843 css_selector = re.escape(css_selector)844 css_selector = self.__escape_quotes_if_needed(css_selector)845 script = ("""document.querySelector('%s').removeAttribute('%s');"""846 % (css_selector, attribute))847 self.execute_script(script)848 def remove_attributes(self, selector, attribute, by=By.CSS_SELECTOR):849 """ This method uses JavaScript to remove a common attribute.850 All matching selectors from querySelectorAll() are used. """851 selector, by = self.__recalculate_selector(selector, by)852 attribute = re.escape(attribute)853 attribute = self.__escape_quotes_if_needed(attribute)854 css_selector = self.convert_to_css_selector(selector, by=by)855 css_selector = re.escape(css_selector)856 css_selector = self.__escape_quotes_if_needed(css_selector)857 script = ("""var $elements = document.querySelectorAll('%s');858 var index = 0, length = $elements.length;859 for(; index < length; index++){860 $elements[index].removeAttribute('%s');}"""861 % (css_selector, attribute))862 try:863 self.execute_script(script)864 except Exception:865 pass866 def get_property_value(self, selector, property, by=By.CSS_SELECTOR,867 timeout=None):868 """ Returns the property value of a page element's computed style.869 Example:870 opacity = self.get_property_value("html body a", "opacity")871 self.assertTrue(float(opacity) > 0, "Element not visible!") """872 if not timeout:873 timeout = settings.SMALL_TIMEOUT874 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:875 timeout = self.__get_new_timeout(timeout)876 selector, by = self.__recalculate_selector(selector, by)877 self.wait_for_ready_state_complete()878 page_actions.wait_for_element_present(879 self.driver, selector, by, timeout)880 try:881 selector = self.convert_to_css_selector(selector, by=by)882 except Exception:883 # Don't run action if can't convert to CSS_Selector for JavaScript884 raise Exception(885 "Exception: Could not convert {%s}(by=%s) to CSS_SELECTOR!" % (886 selector, by))887 selector = re.escape(selector)888 selector = self.__escape_quotes_if_needed(selector)889 script = ("""var $elm = document.querySelector('%s');890 $val = window.getComputedStyle($elm).getPropertyValue('%s');891 return $val;"""892 % (selector, property))893 value = self.execute_script(script)894 if value is not None:895 return value896 else:897 return "" # Return an empty string if the property doesn't exist898 def get_image_url(self, selector, by=By.CSS_SELECTOR, timeout=None):899 """ Extracts the URL from an image element on the page. """900 if not timeout:901 timeout = settings.SMALL_TIMEOUT902 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:903 timeout = self.__get_new_timeout(timeout)904 return self.get_attribute(selector,905 attribute='src', by=by, timeout=timeout)906 def find_elements(self, selector, by=By.CSS_SELECTOR, limit=0):907 """ Returns a list of matching WebElements.908 Elements could be either hidden or visible on the page.909 If "limit" is set and > 0, will only return that many elements. """910 selector, by = self.__recalculate_selector(selector, by)911 self.wait_for_ready_state_complete()912 time.sleep(0.07)913 elements = self.driver.find_elements(by=by, value=selector)914 if limit and limit > 0 and len(elements) > limit:915 elements = elements[:limit]916 return elements917 def find_visible_elements(self, selector, by=By.CSS_SELECTOR, limit=0):918 """ Returns a list of matching WebElements that are visible.919 If "limit" is set and > 0, will only return that many elements. """920 selector, by = self.__recalculate_selector(selector, by)921 self.wait_for_ready_state_complete()922 time.sleep(0.07)923 v_elems = page_actions.find_visible_elements(self.driver, selector, by)924 if limit and limit > 0 and len(v_elems) > limit:925 v_elems = v_elems[:limit]926 return v_elems927 def click_visible_elements(self, selector, by=By.CSS_SELECTOR, limit=0):928 """ Finds all matching page elements and clicks visible ones in order.929 If a click reloads or opens a new page, the clicking will stop.930 If no matching elements appear, an Exception will be raised.931 If "limit" is set and > 0, will only click that many elements.932 Also clicks elements that become visible from previous clicks.933 Works best for actions such as clicking all checkboxes on a page.934 Example: self.click_visible_elements('input[type="checkbox"]') """935 selector, by = self.__recalculate_selector(selector, by)936 self.wait_for_element_present(937 selector, by=by, timeout=settings.SMALL_TIMEOUT)938 elements = self.find_elements(selector, by=by)939 click_count = 0940 for element in elements:941 if limit and limit > 0 and click_count >= limit:942 return943 try:944 if element.is_displayed():945 self.__scroll_to_element(element)946 element.click()947 click_count += 1948 self.wait_for_ready_state_complete()949 except ECI_Exception:950 continue # ElementClickInterceptedException (Overlay likely)951 except (StaleElementReferenceException, ENI_Exception):952 self.wait_for_ready_state_complete()953 time.sleep(0.03)954 try:955 if element.is_displayed():956 self.__scroll_to_element(element)957 element.click()958 click_count += 1959 self.wait_for_ready_state_complete()960 except (StaleElementReferenceException, ENI_Exception):961 return # Probably on new page / Elements are all stale962 def click_nth_visible_element(self, selector, number, by=By.CSS_SELECTOR):963 """ Finds all matching page elements and clicks the nth visible one.964 Example: self.click_nth_visible_element('[type="checkbox"]', 5)965 (Clicks the 5th visible checkbox on the page.) """966 elements = self.find_visible_elements(selector, by=by)967 if len(elements) < number:968 raise Exception("Not enough matching {%s} elements of type {%s} to"969 " click number %s!" % (selector, by, number))970 number = number - 1971 if number < 0:972 number = 0973 element = elements[number]974 self.wait_for_ready_state_complete()975 try:976 self.__scroll_to_element(element)977 element.click()978 except (StaleElementReferenceException, ENI_Exception):979 self.wait_for_ready_state_complete()980 time.sleep(0.05)981 self.__scroll_to_element(element)982 element.click()983 def click_if_visible(self, selector, by=By.CSS_SELECTOR):984 """ If the page selector exists and is visible, clicks on the element.985 This method only clicks on the first matching element found.986 (Use click_visible_elements() to click all matching elements.) """987 self.wait_for_ready_state_complete()988 if self.is_element_visible(selector, by=by):989 self.click(selector, by=by)990 def is_element_in_an_iframe(self, selector, by=By.CSS_SELECTOR):991 """ Returns True if the selector's element is located in an iframe.992 Otherwise returns False. """993 selector, by = self.__recalculate_selector(selector, by)994 if self.is_element_present(selector, by=by):995 return False996 soup = self.get_beautiful_soup()997 iframe_list = soup.select('iframe')998 for iframe in iframe_list:999 iframe_identifier = None1000 if iframe.has_attr('name') and len(iframe['name']) > 0:1001 iframe_identifier = iframe['name']1002 elif iframe.has_attr('id') and len(iframe['id']) > 0:1003 iframe_identifier = iframe['id']1004 elif iframe.has_attr('class') and len(iframe['class']) > 0:1005 iframe_class = " ".join(iframe["class"])1006 iframe_identifier = '[class="%s"]' % iframe_class1007 else:1008 continue1009 self.switch_to_frame(iframe_identifier)1010 if self.is_element_present(selector, by=by):1011 self.switch_to_default_content()1012 return True1013 self.switch_to_default_content()1014 return False1015 def switch_to_frame_of_element(self, selector, by=By.CSS_SELECTOR):1016 """ Set driver control to the iframe containing element (assuming the1017 element is in a single-nested iframe) and returns the iframe name.1018 If element is not in an iframe, returns None, and nothing happens.1019 May not work if multiple iframes are nested within each other. """1020 selector, by = self.__recalculate_selector(selector, by)1021 if self.is_element_present(selector, by=by):1022 return None1023 soup = self.get_beautiful_soup()1024 iframe_list = soup.select('iframe')1025 for iframe in iframe_list:1026 iframe_identifier = None1027 if iframe.has_attr('name') and len(iframe['name']) > 0:1028 iframe_identifier = iframe['name']1029 elif iframe.has_attr('id') and len(iframe['id']) > 0:1030 iframe_identifier = iframe['id']1031 elif iframe.has_attr('class') and len(iframe['class']) > 0:1032 iframe_class = " ".join(iframe["class"])1033 iframe_identifier = '[class="%s"]' % iframe_class1034 else:1035 continue1036 try:1037 self.switch_to_frame(iframe_identifier, timeout=1)1038 if self.is_element_present(selector, by=by):1039 return iframe_identifier1040 except Exception:1041 pass1042 self.switch_to_default_content()1043 try:1044 self.switch_to_frame(selector, timeout=1)1045 return selector1046 except Exception:1047 if self.is_element_present(selector, by=by):1048 return ""1049 raise Exception("Could not switch to iframe containing "1050 "element {%s}!" % selector)1051 def hover_on_element(self, selector, by=By.CSS_SELECTOR):1052 selector, by = self.__recalculate_selector(selector, by)1053 if page_utils.is_xpath_selector(selector):1054 selector = self.convert_to_css_selector(selector, By.XPATH)1055 by = By.CSS_SELECTOR1056 self.wait_for_element_visible(1057 selector, by=by, timeout=settings.SMALL_TIMEOUT)1058 self.__demo_mode_highlight_if_active(selector, by)1059 self.scroll_to(selector, by=by)1060 time.sleep(0.05) # Settle down from scrolling before hovering1061 return page_actions.hover_on_element(self.driver, selector)1062 def hover_and_click(self, hover_selector, click_selector,1063 hover_by=By.CSS_SELECTOR, click_by=By.CSS_SELECTOR,1064 timeout=None):1065 """ When you want to hover over an element or dropdown menu,1066 and then click an element that appears after that. """1067 if not timeout:1068 timeout = settings.SMALL_TIMEOUT1069 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1070 timeout = self.__get_new_timeout(timeout)1071 hover_selector, hover_by = self.__recalculate_selector(1072 hover_selector, hover_by)1073 hover_selector = self.convert_to_css_selector(1074 hover_selector, hover_by)1075 click_selector, click_by = self.__recalculate_selector(1076 click_selector, click_by)1077 dropdown_element = self.wait_for_element_visible(1078 hover_selector, by=hover_by, timeout=timeout)1079 self.__demo_mode_highlight_if_active(hover_selector, hover_by)1080 self.scroll_to(hover_selector, by=hover_by)1081 pre_action_url = self.driver.current_url1082 outdated_driver = False1083 element = None1084 try:1085 page_actions.hover_element(self.driver, dropdown_element)1086 except Exception:1087 outdated_driver = True1088 element = self.wait_for_element_present(1089 click_selector, click_by, timeout)1090 if click_by == By.LINK_TEXT:1091 self.open(self.__get_href_from_link_text(click_selector))1092 elif click_by == By.PARTIAL_LINK_TEXT:1093 self.open(self.__get_href_from_partial_link_text(1094 click_selector))1095 else:1096 self.js_click(click_selector, click_by)1097 if not outdated_driver:1098 element = page_actions.hover_and_click(1099 self.driver, hover_selector, click_selector,1100 hover_by, click_by, timeout)1101 if self.demo_mode:1102 if self.driver.current_url != pre_action_url:1103 self.__demo_mode_pause_if_active()1104 else:1105 self.__demo_mode_pause_if_active(tiny=True)1106 elif self.slow_mode:1107 self.__slow_mode_pause_if_active()1108 return element1109 def hover_and_double_click(self, hover_selector, click_selector,1110 hover_by=By.CSS_SELECTOR,1111 click_by=By.CSS_SELECTOR,1112 timeout=None):1113 """ When you want to hover over an element or dropdown menu,1114 and then double-click an element that appears after that. """1115 if not timeout:1116 timeout = settings.SMALL_TIMEOUT1117 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1118 timeout = self.__get_new_timeout(timeout)1119 hover_selector, hover_by = self.__recalculate_selector(1120 hover_selector, hover_by)1121 hover_selector = self.convert_to_css_selector(1122 hover_selector, hover_by)1123 click_selector, click_by = self.__recalculate_selector(1124 click_selector, click_by)1125 dropdown_element = self.wait_for_element_visible(1126 hover_selector, by=hover_by, timeout=timeout)1127 self.__demo_mode_highlight_if_active(hover_selector, hover_by)1128 self.scroll_to(hover_selector, by=hover_by)1129 pre_action_url = self.driver.current_url1130 outdated_driver = False1131 element = None1132 try:1133 page_actions.hover_element(self.driver, dropdown_element)1134 except Exception:1135 outdated_driver = True1136 element = self.wait_for_element_present(1137 click_selector, click_by, timeout)1138 if click_by == By.LINK_TEXT:1139 self.open(self.__get_href_from_link_text(click_selector))1140 elif click_by == By.PARTIAL_LINK_TEXT:1141 self.open(self.__get_href_from_partial_link_text(1142 click_selector))1143 else:1144 self.js_click(click_selector, click_by)1145 if not outdated_driver:1146 element = page_actions.hover_element_and_double_click(1147 self.driver, dropdown_element, click_selector,1148 click_by=By.CSS_SELECTOR, timeout=timeout)1149 if self.demo_mode:1150 if self.driver.current_url != pre_action_url:1151 self.__demo_mode_pause_if_active()1152 else:1153 self.__demo_mode_pause_if_active(tiny=True)1154 elif self.slow_mode:1155 self.__slow_mode_pause_if_active()1156 return element1157 def __select_option(self, dropdown_selector, option,1158 dropdown_by=By.CSS_SELECTOR, option_by="text",1159 timeout=None):1160 """ Selects an HTML <select> option by specification.1161 Option specifications are by "text", "index", or "value".1162 Defaults to "text" if option_by is unspecified or unknown. """1163 if not timeout:1164 timeout = settings.SMALL_TIMEOUT1165 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1166 timeout = self.__get_new_timeout(timeout)1167 if page_utils.is_xpath_selector(dropdown_selector):1168 dropdown_by = By.XPATH1169 self.wait_for_ready_state_complete()1170 element = self.wait_for_element_present(1171 dropdown_selector, by=dropdown_by, timeout=timeout)1172 if self.is_element_visible(dropdown_selector, by=dropdown_by):1173 self.__demo_mode_highlight_if_active(1174 dropdown_selector, dropdown_by)1175 pre_action_url = self.driver.current_url1176 try:1177 if option_by == "index":1178 Select(element).select_by_index(option)1179 elif option_by == "value":1180 Select(element).select_by_value(option)1181 else:1182 Select(element).select_by_visible_text(option)1183 except (StaleElementReferenceException, ENI_Exception):1184 self.wait_for_ready_state_complete()1185 time.sleep(0.05)1186 element = self.wait_for_element_present(1187 dropdown_selector, by=dropdown_by, timeout=timeout)1188 if option_by == "index":1189 Select(element).select_by_index(option)1190 elif option_by == "value":1191 Select(element).select_by_value(option)1192 else:1193 Select(element).select_by_visible_text(option)1194 if settings.WAIT_FOR_RSC_ON_CLICKS:1195 self.wait_for_ready_state_complete()1196 if self.demo_mode:1197 if self.driver.current_url != pre_action_url:1198 self.__demo_mode_pause_if_active()1199 else:1200 self.__demo_mode_pause_if_active(tiny=True)1201 elif self.slow_mode:1202 self.__slow_mode_pause_if_active()1203 def select_option_by_text(self, dropdown_selector, option,1204 dropdown_by=By.CSS_SELECTOR,1205 timeout=None):1206 """ Selects an HTML <select> option by option text.1207 @Params1208 dropdown_selector - the <select> selector1209 option - the text of the option """1210 if not timeout:1211 timeout = settings.SMALL_TIMEOUT1212 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1213 timeout = self.__get_new_timeout(timeout)1214 self.__select_option(dropdown_selector, option,1215 dropdown_by=dropdown_by, option_by="text",1216 timeout=timeout)1217 def select_option_by_index(self, dropdown_selector, option,1218 dropdown_by=By.CSS_SELECTOR,1219 timeout=None):1220 """ Selects an HTML <select> option by option index.1221 @Params1222 dropdown_selector - the <select> selector1223 option - the index number of the option """1224 if not timeout:1225 timeout = settings.SMALL_TIMEOUT1226 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1227 timeout = self.__get_new_timeout(timeout)1228 self.__select_option(dropdown_selector, option,1229 dropdown_by=dropdown_by, option_by="index",1230 timeout=timeout)1231 def select_option_by_value(self, dropdown_selector, option,1232 dropdown_by=By.CSS_SELECTOR,1233 timeout=None):1234 """ Selects an HTML <select> option by option value.1235 @Params1236 dropdown_selector - the <select> selector1237 option - the value property of the option """1238 if not timeout:1239 timeout = settings.SMALL_TIMEOUT1240 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1241 timeout = self.__get_new_timeout(timeout)1242 self.__select_option(dropdown_selector, option,1243 dropdown_by=dropdown_by, option_by="value",1244 timeout=timeout)1245 def execute_script(self, script):1246 return self.driver.execute_script(script)1247 def execute_async_script(self, script, timeout=None):1248 if not timeout:1249 timeout = settings.EXTREME_TIMEOUT1250 return js_utils.execute_async_script(self.driver, script, timeout)1251 def safe_execute_script(self, script):1252 """ When executing a script that contains a jQuery command,1253 it's important that the jQuery library has been loaded first.1254 This method will load jQuery if it wasn't already loaded. """1255 try:1256 self.execute_script(script)1257 except Exception:1258 # The likely reason this fails is because: "jQuery is not defined"1259 self.activate_jquery() # It's a good thing we can define it here1260 self.execute_script(script)1261 def set_window_size(self, width, height):1262 self.driver.set_window_size(width, height)1263 self.__demo_mode_pause_if_active()1264 def maximize_window(self):1265 self.driver.maximize_window()1266 self.__demo_mode_pause_if_active()1267 def switch_to_frame(self, frame, timeout=None):1268 """1269 Wait for an iframe to appear, and switch to it. This should be1270 usable as a drop-in replacement for driver.switch_to.frame().1271 @Params1272 frame - the frame element, name, id, index, or selector1273 timeout - the time to wait for the alert in seconds1274 """1275 if not timeout:1276 timeout = settings.SMALL_TIMEOUT1277 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1278 timeout = self.__get_new_timeout(timeout)1279 page_actions.switch_to_frame(self.driver, frame, timeout)1280 def switch_to_default_content(self):1281 """ Brings driver control outside the current iframe.1282 (If driver control is inside an iframe, the driver control1283 will be set to one level above the current frame. If the driver1284 control is not currenly in an iframe, nothing will happen.) """1285 self.driver.switch_to.default_content()1286 def open_new_window(self, switch_to=True):1287 """ Opens a new browser tab/window and switches to it by default. """1288 self.driver.execute_script("window.open('');")1289 time.sleep(0.01)1290 if switch_to:1291 self.switch_to_window(len(self.driver.window_handles) - 1)1292 def switch_to_window(self, window, timeout=None):1293 if not timeout:1294 timeout = settings.SMALL_TIMEOUT1295 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1296 timeout = self.__get_new_timeout(timeout)1297 page_actions.switch_to_window(self.driver, window, timeout)1298 def switch_to_default_window(self):1299 self.switch_to_window(0)1300 def get_new_driver(self, browser=None, headless=None,1301 servername=None, port=None, proxy=None, agent=None,1302 switch_to=True, cap_file=None, disable_csp=None,1303 enable_sync=None, incognito=None, user_data_dir=None,1304 extension_zip=None, extension_dir=None, is_mobile=False,1305 d_width=None, d_height=None, d_p_r=None):1306 """ This method spins up an extra browser for tests that require1307 more than one. The first browser is already provided by tests1308 that import base_case.BaseCase from seleniumbase. If parameters1309 aren't specified, the method uses the same as the default driver.1310 @Params1311 browser - the browser to use. (Ex: "chrome", "firefox")1312 headless - the option to run webdriver in headless mode1313 servername - if using a Selenium Grid, set the host address here1314 port - if using a Selenium Grid, set the host port here1315 proxy - if using a proxy server, specify the "host:port" combo here1316 switch_to - the option to switch to the new driver (default = True)1317 cap_file - the file containing desired capabilities for the browser1318 disable_csp - an option to disable Chrome's Content Security Policy1319 enable_sync - the option to enable the Chrome Sync feature (Chrome)1320 incognito - the option to enable Chrome's Incognito mode (Chrome)1321 user_data_dir - Chrome's User Data Directory to use (Chrome-only)1322 extension_zip - A Chrome Extension ZIP file to use (Chrome-only)1323 extension_dir - A Chrome Extension folder to use (Chrome-only)1324 is_mobile - the option to use the mobile emulator (Chrome-only)1325 d_width - the device width of the mobile emulator (Chrome-only)1326 d_height - the device height of the mobile emulator (Chrome-only)1327 d_p_r - the device pixel ratio of the mobile emulator (Chrome-only)1328 """1329 if self.browser == "remote" and self.servername == "localhost":1330 raise Exception('Cannot use "remote" browser driver on localhost!'1331 ' Did you mean to connect to a remote Grid server'1332 ' such as BrowserStack or Sauce Labs? In that'1333 ' case, you must specify the "server" and "port"'1334 ' parameters on the command line! '1335 'Example: '1336 '--server=user:key@hub.browserstack.com --port=80')1337 browserstack_ref = (1338 'https://browserstack.com/automate/capabilities')1339 sauce_labs_ref = (1340 'https://wiki.saucelabs.com/display/DOCS/Platform+Configurator#/')1341 if self.browser == "remote" and not self.cap_file:1342 raise Exception('Need to specify a desired capabilities file when '1343 'using "--browser=remote". Add "--cap_file=FILE". '1344 'File should be in the Python format used by: '1345 '%s OR '1346 '%s '1347 'See SeleniumBase/examples/sample_cap_file_BS.py '1348 'and SeleniumBase/examples/sample_cap_file_SL.py'1349 % (browserstack_ref, sauce_labs_ref))1350 if browser is None:1351 browser = self.browser1352 browser_name = browser1353 if headless is None:1354 headless = self.headless1355 if servername is None:1356 servername = self.servername1357 if port is None:1358 port = self.port1359 use_grid = False1360 if servername != "localhost":1361 # Use Selenium Grid (Use "127.0.0.1" for localhost Grid)1362 use_grid = True1363 proxy_string = proxy1364 if proxy_string is None:1365 proxy_string = self.proxy_string1366 user_agent = agent1367 if user_agent is None:1368 user_agent = self.user_agent1369 if disable_csp is None:1370 disable_csp = self.disable_csp1371 if enable_sync is None:1372 enable_sync = self.enable_sync1373 if incognito is None:1374 incognito = self.incognito1375 if user_data_dir is None:1376 user_data_dir = self.user_data_dir1377 if extension_zip is None:1378 extension_zip = self.extension_zip1379 if extension_dir is None:1380 extension_dir = self.extension_dir1381 # Due to https://stackoverflow.com/questions/23055651/ , skip extension1382 # if self.demo_mode or self.masterqa_mode:1383 # disable_csp = True1384 if cap_file is None:1385 cap_file = self.cap_file1386 if is_mobile is None:1387 is_mobile = False1388 if d_width is None:1389 d_width = self.__device_width1390 if d_height is None:1391 d_height = self.__device_height1392 if d_p_r is None:1393 d_p_r = self.__device_pixel_ratio1394 valid_browsers = constants.ValidBrowsers.valid_browsers1395 if browser_name not in valid_browsers:1396 raise Exception("Browser: {%s} is not a valid browser option. "1397 "Valid options = {%s}" % (browser, valid_browsers))1398 # Launch a web browser1399 from seleniumbase.core import browser_launcher1400 new_driver = browser_launcher.get_driver(browser_name=browser_name,1401 headless=headless,1402 use_grid=use_grid,1403 servername=servername,1404 port=port,1405 proxy_string=proxy_string,1406 user_agent=user_agent,1407 cap_file=cap_file,1408 disable_csp=disable_csp,1409 enable_sync=enable_sync,1410 incognito=incognito,1411 user_data_dir=user_data_dir,1412 extension_zip=extension_zip,1413 extension_dir=extension_dir,1414 mobile_emulator=is_mobile,1415 device_width=d_width,1416 device_height=d_height,1417 device_pixel_ratio=d_p_r)1418 self._drivers_list.append(new_driver)1419 if switch_to:1420 self.driver = new_driver1421 if self.headless:1422 # Make sure the invisible browser window is big enough1423 width = settings.HEADLESS_START_WIDTH1424 height = settings.HEADLESS_START_HEIGHT1425 try:1426 self.set_window_size(width, height)1427 self.wait_for_ready_state_complete()1428 except Exception:1429 # This shouldn't fail, but in case it does,1430 # get safely through setUp() so that1431 # WebDrivers can get closed during tearDown().1432 pass1433 else:1434 if self.browser == 'chrome':1435 width = settings.CHROME_START_WIDTH1436 height = settings.CHROME_START_HEIGHT1437 try:1438 if self.maximize_option:1439 self.driver.maximize_window()1440 else:1441 self.driver.set_window_size(width, height)1442 self.wait_for_ready_state_complete()1443 except Exception:1444 pass # Keep existing browser resolution1445 elif self.browser == 'firefox':1446 pass # No changes1447 elif self.browser == 'safari':1448 if self.maximize_option:1449 try:1450 self.driver.maximize_window()1451 self.wait_for_ready_state_complete()1452 except Exception:1453 pass # Keep existing browser resolution1454 elif self.browser == 'edge':1455 try:1456 self.driver.maximize_window()1457 self.wait_for_ready_state_complete()1458 except Exception:1459 pass # Keep existing browser resolution1460 if self.start_page and len(self.start_page) >= 4:1461 if page_utils.is_valid_url(self.start_page):1462 self.open(self.start_page)1463 else:1464 new_start_page = "http://" + self.start_page1465 if page_utils.is_valid_url(new_start_page):1466 self.open(new_start_page)1467 return new_driver1468 def switch_to_driver(self, driver):1469 """ Sets self.driver to the specified driver.1470 You may need this if using self.get_new_driver() in your code. """1471 self.driver = driver1472 def switch_to_default_driver(self):1473 """ Sets self.driver to the default/original driver. """1474 self.driver = self._default_driver1475 def save_screenshot(self, name, folder=None):1476 """ The screenshot will be in PNG format. """1477 return page_actions.save_screenshot(self.driver, name, folder)1478 def save_page_source(self, name, folder=None):1479 """ Saves the page HTML to the current directory (or given subfolder).1480 If the folder specified doesn't exist, it will get created.1481 @Params1482 name - The file name to save the current page's HTML to.1483 folder - The folder to save the file to. (Default = current folder)1484 """1485 return page_actions.save_page_source(self.driver, name, folder)1486 def save_cookies(self, name="cookies.txt"):1487 """ Saves the page cookies to the "saved_cookies" folder. """1488 cookies = self.driver.get_cookies()1489 json_cookies = json.dumps(cookies)1490 if name.endswith('/'):1491 raise Exception("Invalid filename for Cookies!")1492 if '/' in name:1493 name = name.split('/')[-1]1494 if len(name) < 1:1495 raise Exception("Filename for Cookies is too short!")1496 if not name.endswith(".txt"):1497 name = name + ".txt"1498 folder = constants.SavedCookies.STORAGE_FOLDER1499 abs_path = os.path.abspath('.')1500 file_path = abs_path + "/%s" % folder1501 if not os.path.exists(file_path):1502 os.makedirs(file_path)1503 cookies_file_path = "%s/%s" % (file_path, name)1504 cookies_file = codecs.open(cookies_file_path, "w+")1505 cookies_file.writelines(json_cookies)1506 cookies_file.close()1507 def load_cookies(self, name="cookies.txt"):1508 """ Loads the page cookies from the "saved_cookies" folder. """1509 if name.endswith('/'):1510 raise Exception("Invalid filename for Cookies!")1511 if '/' in name:1512 name = name.split('/')[-1]1513 if len(name) < 1:1514 raise Exception("Filename for Cookies is too short!")1515 if not name.endswith(".txt"):1516 name = name + ".txt"1517 folder = constants.SavedCookies.STORAGE_FOLDER1518 abs_path = os.path.abspath('.')1519 file_path = abs_path + "/%s" % folder1520 cookies_file_path = "%s/%s" % (file_path, name)1521 f = open(cookies_file_path, 'r')1522 json_cookies = f.read().strip()1523 f.close()1524 cookies = json.loads(json_cookies)1525 for cookie in cookies:1526 if 'expiry' in cookie:1527 del cookie['expiry']1528 self.driver.add_cookie(cookie)1529 def delete_all_cookies(self):1530 """ Deletes all cookies in the web browser.1531 Does NOT delete the saved cookies file. """1532 self.driver.delete_all_cookies()1533 def delete_saved_cookies(self, name="cookies.txt"):1534 """ Deletes the cookies file from the "saved_cookies" folder.1535 Does NOT delete the cookies from the web browser. """1536 if name.endswith('/'):1537 raise Exception("Invalid filename for Cookies!")1538 if '/' in name:1539 name = name.split('/')[-1]1540 if len(name) < 1:1541 raise Exception("Filename for Cookies is too short!")1542 if not name.endswith(".txt"):1543 name = name + ".txt"1544 folder = constants.SavedCookies.STORAGE_FOLDER1545 abs_path = os.path.abspath('.')1546 file_path = abs_path + "/%s" % folder1547 cookies_file_path = "%s/%s" % (file_path, name)1548 if os.path.exists(cookies_file_path):1549 if cookies_file_path.endswith('.txt'):1550 os.remove(cookies_file_path)1551 def wait_for_ready_state_complete(self, timeout=None):1552 try:1553 # If there's an alert, skip1554 self.driver.switch_to.alert1555 return1556 except Exception:1557 # If there's no alert, continue1558 pass1559 if not timeout:1560 timeout = settings.EXTREME_TIMEOUT1561 if self.timeout_multiplier and timeout == settings.EXTREME_TIMEOUT:1562 timeout = self.__get_new_timeout(timeout)1563 is_ready = js_utils.wait_for_ready_state_complete(self.driver, timeout)1564 self.wait_for_angularjs(timeout=settings.MINI_TIMEOUT)1565 if self.js_checking_on:1566 self.assert_no_js_errors()1567 if self.ad_block_on:1568 # If the ad_block feature is enabled, then block ads for new URLs1569 current_url = self.get_current_url()1570 if not current_url == self.__last_page_load_url:1571 time.sleep(0.02)1572 self.ad_block()1573 time.sleep(0.01)1574 if self.is_element_present("iframe"):1575 time.sleep(0.07) # iframe ads take slightly longer to load1576 self.ad_block() # Do ad_block on slower-loading iframes1577 self.__last_page_load_url = current_url1578 return is_ready1579 def wait_for_angularjs(self, timeout=None, **kwargs):1580 if not timeout:1581 timeout = settings.LARGE_TIMEOUT1582 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:1583 timeout = self.__get_new_timeout(timeout)1584 js_utils.wait_for_angularjs(self.driver, timeout, **kwargs)1585 def sleep(self, seconds):1586 if not sb_config.time_limit:1587 time.sleep(seconds)1588 else:1589 start_ms = time.time() * 1000.01590 stop_ms = start_ms + (seconds * 1000.0)1591 for x in range(int(seconds * 5)):1592 shared_utils.check_if_time_limit_exceeded()1593 now_ms = time.time() * 1000.01594 if now_ms >= stop_ms:1595 break1596 time.sleep(0.2)1597 def activate_jquery(self):1598 """ If "jQuery is not defined", use this method to activate it for use.1599 This happens because jQuery is not always defined on web sites. """1600 js_utils.activate_jquery(self.driver)1601 def __are_quotes_escaped(self, string):1602 return js_utils.are_quotes_escaped(string)1603 def __escape_quotes_if_needed(self, string):1604 return js_utils.escape_quotes_if_needed(string)1605 def bring_to_front(self, selector, by=By.CSS_SELECTOR):1606 """ Updates the Z-index of a page element to bring it into view.1607 Useful when getting a WebDriverException, such as the one below:1608 { Element is not clickable at point (#, #).1609 Other element would receive the click: ... } """1610 if page_utils.is_xpath_selector(selector):1611 by = By.XPATH1612 self.wait_for_element_visible(1613 selector, by=by, timeout=settings.SMALL_TIMEOUT)1614 try:1615 selector = self.convert_to_css_selector(selector, by=by)1616 except Exception:1617 # Don't run action if can't convert to CSS_Selector for JavaScript1618 return1619 selector = re.escape(selector)1620 selector = self.__escape_quotes_if_needed(selector)1621 script = ("""document.querySelector('%s').style.zIndex = '9999';"""1622 % selector)1623 self.execute_script(script)1624 def highlight_click(self, selector, by=By.CSS_SELECTOR,1625 loops=3, scroll=True):1626 if not self.demo_mode:1627 self.highlight(selector, by=by, loops=loops, scroll=scroll)1628 self.click(selector, by=by)1629 def highlight_update_text(self, selector, new_value, by=By.CSS_SELECTOR,1630 loops=3, scroll=True):1631 if not self.demo_mode:1632 self.highlight(selector, by=by, loops=loops, scroll=scroll)1633 self.update_text(selector, new_value, by=by)1634 def highlight(self, selector, by=By.CSS_SELECTOR,1635 loops=settings.HIGHLIGHTS, scroll=True):1636 """ This method uses fancy JavaScript to highlight an element.1637 Used during demo_mode.1638 @Params1639 selector - the selector of the element to find1640 by - the type of selector to search by (Default: CSS)1641 loops - # of times to repeat the highlight animation1642 (Default: 4. Each loop lasts for about 0.18s)1643 scroll - the option to scroll to the element first (Default: True)1644 """1645 selector, by = self.__recalculate_selector(selector, by)1646 element = self.wait_for_element_visible(1647 selector, by=by, timeout=settings.SMALL_TIMEOUT)1648 if scroll:1649 try:1650 self.__slow_scroll_to_element(element)1651 except (StaleElementReferenceException, ENI_Exception):1652 self.wait_for_ready_state_complete()1653 time.sleep(0.05)1654 element = self.wait_for_element_visible(1655 selector, by=by, timeout=settings.SMALL_TIMEOUT)1656 self.__slow_scroll_to_element(element)1657 try:1658 selector = self.convert_to_css_selector(selector, by=by)1659 except Exception:1660 # Don't highlight if can't convert to CSS_SELECTOR1661 return1662 if self.highlights:1663 loops = self.highlights1664 if self.browser == 'ie':1665 loops = 1 # Override previous setting because IE is slow1666 loops = int(loops)1667 o_bs = '' # original_box_shadow1668 try:1669 style = element.get_attribute('style')1670 except (StaleElementReferenceException, ENI_Exception):1671 self.wait_for_ready_state_complete()1672 time.sleep(0.05)1673 element = self.wait_for_element_visible(1674 selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT)1675 style = element.get_attribute('style')1676 if style:1677 if 'box-shadow: ' in style:1678 box_start = style.find('box-shadow: ')1679 box_end = style.find(';', box_start) + 11680 original_box_shadow = style[box_start:box_end]1681 o_bs = original_box_shadow1682 if ":contains" not in selector and ":first" not in selector:1683 selector = re.escape(selector)1684 selector = self.__escape_quotes_if_needed(selector)1685 self.__highlight_with_js(selector, loops, o_bs)1686 else:1687 selector = self.__make_css_match_first_element_only(selector)1688 selector = re.escape(selector)1689 selector = self.__escape_quotes_if_needed(selector)1690 try:1691 self.__highlight_with_jquery(selector, loops, o_bs)1692 except Exception:1693 pass # JQuery probably couldn't load. Skip highlighting.1694 time.sleep(0.065)1695 def __highlight_with_js(self, selector, loops, o_bs):1696 js_utils.highlight_with_js(self.driver, selector, loops, o_bs)1697 def __highlight_with_jquery(self, selector, loops, o_bs):1698 js_utils.highlight_with_jquery(self.driver, selector, loops, o_bs)1699 def scroll_to(self, selector, by=By.CSS_SELECTOR, timeout=None):1700 ''' Fast scroll to destination '''1701 if not timeout:1702 timeout = settings.SMALL_TIMEOUT1703 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1704 timeout = self.__get_new_timeout(timeout)1705 if self.demo_mode or self.slow_mode:1706 self.slow_scroll_to(selector, by=by, timeout=timeout)1707 return1708 element = self.wait_for_element_visible(1709 selector, by=by, timeout=timeout)1710 try:1711 self.__scroll_to_element(element)1712 except (StaleElementReferenceException, ENI_Exception):1713 self.wait_for_ready_state_complete()1714 time.sleep(0.05)1715 element = self.wait_for_element_visible(1716 selector, by=by, timeout=timeout)1717 self.__scroll_to_element(element)1718 def slow_scroll_to(self, selector, by=By.CSS_SELECTOR, timeout=None):1719 ''' Slow motion scroll to destination '''1720 if not timeout:1721 timeout = settings.SMALL_TIMEOUT1722 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1723 timeout = self.__get_new_timeout(timeout)1724 selector, by = self.__recalculate_selector(selector, by)1725 element = self.wait_for_element_visible(1726 selector, by=by, timeout=timeout)1727 try:1728 self.__slow_scroll_to_element(element)1729 except (StaleElementReferenceException, ENI_Exception):1730 self.wait_for_ready_state_complete()1731 time.sleep(0.05)1732 element = self.wait_for_element_visible(1733 selector, by=by, timeout=timeout)1734 self.__slow_scroll_to_element(element)1735 def click_xpath(self, xpath):1736 # Technically self.click() will automatically detect an xpath selector,1737 # so self.click_xpath() is just a longer name for the same action.1738 self.click(xpath, by=By.XPATH)1739 def js_click(self, selector, by=By.CSS_SELECTOR, all_matches=False):1740 """ Clicks an element using pure JS. Does not use jQuery.1741 If "all_matches" is False, only the first match is clicked. """1742 selector, by = self.__recalculate_selector(selector, by)1743 if by == By.LINK_TEXT:1744 message = (1745 "Pure JavaScript doesn't support clicking by Link Text. "1746 "You may want to use self.jquery_click() instead, which "1747 "allows this with :contains(), assuming jQuery isn't blocked. "1748 "For now, self.js_click() will use a regular WebDriver click.")1749 logging.debug(message)1750 self.click(selector, by=by)1751 return1752 element = self.wait_for_element_present(1753 selector, by=by, timeout=settings.SMALL_TIMEOUT)1754 if self.is_element_visible(selector, by=by):1755 self.__demo_mode_highlight_if_active(selector, by)1756 if not self.demo_mode:1757 self.__scroll_to_element(element)1758 css_selector = self.convert_to_css_selector(selector, by=by)1759 css_selector = re.escape(css_selector)1760 css_selector = self.__escape_quotes_if_needed(css_selector)1761 if not all_matches:1762 self.__js_click(selector, by=by) # The real "magic" happens1763 else:1764 self.__js_click_all(selector, by=by) # The real "magic" happens1765 self.__demo_mode_pause_if_active()1766 def js_click_all(self, selector, by=By.CSS_SELECTOR):1767 """ Clicks all matching elements using pure JS. (No jQuery) """1768 self.js_click(selector, by=By.CSS_SELECTOR, all_matches=True)1769 def jquery_click(self, selector, by=By.CSS_SELECTOR):1770 """ Clicks an element using jQuery. Different from using pure JS. """1771 selector, by = self.__recalculate_selector(selector, by)1772 self.wait_for_element_present(1773 selector, by=by, timeout=settings.SMALL_TIMEOUT)1774 if self.is_element_visible(selector, by=by):1775 self.__demo_mode_highlight_if_active(selector, by)1776 selector = self.convert_to_css_selector(selector, by=by)1777 selector = self.__make_css_match_first_element_only(selector)1778 click_script = """jQuery('%s')[0].click()""" % selector1779 self.safe_execute_script(click_script)1780 self.__demo_mode_pause_if_active()1781 def jquery_click_all(self, selector, by=By.CSS_SELECTOR):1782 """ Clicks all matching elements using jQuery. """1783 selector, by = self.__recalculate_selector(selector, by)1784 self.wait_for_element_present(1785 selector, by=by, timeout=settings.SMALL_TIMEOUT)1786 if self.is_element_visible(selector, by=by):1787 self.__demo_mode_highlight_if_active(selector, by)1788 selector = self.convert_to_css_selector(selector, by=by)1789 click_script = """jQuery('%s').click()""" % selector1790 self.safe_execute_script(click_script)1791 self.__demo_mode_pause_if_active()1792 def hide_element(self, selector, by=By.CSS_SELECTOR):1793 """ Hide the first element on the page that matches the selector. """1794 selector, by = self.__recalculate_selector(selector, by)1795 selector = self.convert_to_css_selector(selector, by=by)1796 selector = self.__make_css_match_first_element_only(selector)1797 hide_script = """jQuery('%s').hide()""" % selector1798 self.safe_execute_script(hide_script)1799 def hide_elements(self, selector, by=By.CSS_SELECTOR):1800 """ Hide all elements on the page that match the selector. """1801 selector, by = self.__recalculate_selector(selector, by)1802 selector = self.convert_to_css_selector(selector, by=by)1803 hide_script = """jQuery('%s').hide()""" % selector1804 self.safe_execute_script(hide_script)1805 def show_element(self, selector, by=By.CSS_SELECTOR):1806 """ Show the first element on the page that matches the selector. """1807 selector, by = self.__recalculate_selector(selector, by)1808 selector = self.convert_to_css_selector(selector, by=by)1809 selector = self.__make_css_match_first_element_only(selector)1810 show_script = """jQuery('%s').show(0)""" % selector1811 self.safe_execute_script(show_script)1812 def show_elements(self, selector, by=By.CSS_SELECTOR):1813 """ Show all elements on the page that match the selector. """1814 selector, by = self.__recalculate_selector(selector, by)1815 selector = self.convert_to_css_selector(selector, by=by)1816 show_script = """jQuery('%s').show(0)""" % selector1817 self.safe_execute_script(show_script)1818 def remove_element(self, selector, by=By.CSS_SELECTOR):1819 """ Remove the first element on the page that matches the selector. """1820 selector, by = self.__recalculate_selector(selector, by)1821 selector = self.convert_to_css_selector(selector, by=by)1822 selector = self.__make_css_match_first_element_only(selector)1823 remove_script = """jQuery('%s').remove()""" % selector1824 self.safe_execute_script(remove_script)1825 def remove_elements(self, selector, by=By.CSS_SELECTOR):1826 """ Remove all elements on the page that match the selector. """1827 selector, by = self.__recalculate_selector(selector, by)1828 selector = self.convert_to_css_selector(selector, by=by)1829 remove_script = """jQuery('%s').remove()""" % selector1830 self.safe_execute_script(remove_script)1831 def ad_block(self):1832 self.wait_for_ready_state_complete()1833 from seleniumbase.config import ad_block_list1834 for css_selector in ad_block_list.AD_BLOCK_LIST:1835 css_selector = re.escape(css_selector)1836 css_selector = self.__escape_quotes_if_needed(css_selector)1837 script = ("""var $elements = document.querySelectorAll('%s');1838 var index = 0, length = $elements.length;1839 for(; index < length; index++){1840 $elements[index].remove();}"""1841 % css_selector)1842 try:1843 self.execute_script(script)1844 except Exception:1845 pass # Don't fail test if ad_blocking fails1846 def get_domain_url(self, url):1847 return page_utils.get_domain_url(url)1848 def get_beautiful_soup(self, source=None):1849 """ BeautifulSoup is a toolkit for dissecting an HTML document1850 and extracting what you need. It's great for screen-scraping! """1851 from bs4 import BeautifulSoup1852 if not source:1853 self.wait_for_ready_state_complete()1854 source = self.get_page_source()1855 soup = BeautifulSoup(source, "html.parser")1856 return soup1857 def get_unique_links(self):1858 """ Get all unique links in the html of the page source.1859 Page links include those obtained from:1860 "a"->"href", "img"->"src", "link"->"href", and "script"->"src". """1861 page_url = self.get_current_url()1862 soup = self.get_beautiful_soup(self.get_page_source())1863 links = page_utils._get_unique_links(page_url, soup)1864 return links1865 def get_link_status_code(self, link, allow_redirects=False, timeout=5):1866 """ Get the status code of a link.1867 If the timeout is exceeded, will return a 404.1868 For a list of available status codes, see:1869 https://en.wikipedia.org/wiki/List_of_HTTP_status_codes """1870 status_code = page_utils._get_link_status_code(1871 link, allow_redirects=allow_redirects, timeout=timeout)1872 return status_code1873 def assert_link_status_code_is_not_404(self, link):1874 status_code = str(self.get_link_status_code(link))1875 bad_link_str = 'Error: "%s" returned a 404!' % link1876 self.assertNotEqual(status_code, "404", bad_link_str)1877 def assert_no_404_errors(self, multithreaded=True):1878 """ Assert no 404 errors from page links obtained from:1879 "a"->"href", "img"->"src", "link"->"href", and "script"->"src". """1880 all_links = self.get_unique_links()1881 links = []1882 for link in all_links:1883 if "javascript:" not in link and "mailto:" not in link:1884 links.append(link)1885 if multithreaded:1886 from multiprocessing.dummy import Pool as ThreadPool1887 pool = ThreadPool(10)1888 pool.map(self.assert_link_status_code_is_not_404, links)1889 pool.close()1890 pool.join()1891 else:1892 for link in links:1893 self.assert_link_status_code_is_not_404(link)1894 if self.demo_mode:1895 messenger_post = ("ASSERT NO 404 ERRORS")1896 self.__highlight_with_assert_success(messenger_post, "html")1897 def print_unique_links_with_status_codes(self):1898 """ Finds all unique links in the html of the page source1899 and then prints out those links with their status codes.1900 Format: ["link" -> "status_code"] (per line)1901 Page links include those obtained from:1902 "a"->"href", "img"->"src", "link"->"href", and "script"->"src". """1903 page_url = self.get_current_url()1904 soup = self.get_beautiful_soup(self.get_page_source())1905 page_utils._print_unique_links_with_status_codes(page_url, soup)1906 def __fix_unicode_conversion(self, text):1907 """ Fixing Chinese characters when converting from PDF to HTML. """1908 text = text.replace(u'\u2f8f', u'\u884c')1909 text = text.replace(u'\u2f45', u'\u65b9')1910 text = text.replace(u'\u2f08', u'\u4eba')1911 text = text.replace(u'\u2f70', u'\u793a')1912 return text1913 def get_pdf_text(self, pdf, page=None, maxpages=None,1914 password=None, codec='utf-8', wrap=False, nav=False,1915 override=False):1916 """ Gets text from a PDF file.1917 PDF can be either a URL or a file path on the local file system.1918 @Params1919 pdf - The URL or file path of the PDF file.1920 page - The page number (or a list of page numbers) of the PDF.1921 If a page number is provided, looks only at that page.1922 (1 is the first page, 2 is the second page, etc.)1923 If no page number is provided, returns all PDF text.1924 maxpages - Instead of providing a page number, you can provide1925 the number of pages to use from the beginning.1926 password - If the PDF is password-protected, enter it here.1927 codec - The compression format for character encoding.1928 (The default codec used by this method is 'utf-8'.)1929 wrap - Replaces ' \n' with ' ' so that individual sentences1930 from a PDF don't get broken up into seperate lines when1931 getting converted into text format.1932 nav - If PDF is a URL, navigates to the URL in the browser first.1933 (Not needed because the PDF will be downloaded anyway.)1934 override - If the PDF file to be downloaded already exists in the1935 downloaded_files/ folder, that PDF will be used1936 instead of downloading it again. """1937 from pdfminer.high_level import extract_text1938 if not password:1939 password = ''1940 if not maxpages:1941 maxpages = 01942 if not pdf.lower().endswith('.pdf'):1943 raise Exception("%s is not a PDF file! (Expecting a .pdf)" % pdf)1944 file_path = None1945 if page_utils.is_valid_url(pdf):1946 if nav:1947 if self.get_current_url() != pdf:1948 self.open(pdf)1949 file_name = pdf.split('/')[-1]1950 file_path = self.get_downloads_folder() + '/' + file_name1951 if not os.path.exists(file_path):1952 self.download_file(pdf)1953 elif override:1954 self.download_file(pdf)1955 else:1956 if not os.path.exists(pdf):1957 raise Exception("%s is not a valid URL or file path!" % pdf)1958 file_path = os.path.abspath(pdf)1959 page_search = None # (Pages are delimited by '\x0c')1960 if type(page) is list:1961 pages = page1962 page_search = []1963 for page in pages:1964 page_search.append(page - 1)1965 elif type(page) is int:1966 page = page - 11967 if page < 0:1968 page = 01969 page_search = [page]1970 else:1971 page_search = None1972 pdf_text = extract_text(1973 file_path, password='', page_numbers=page_search,1974 maxpages=maxpages, caching=False, codec=codec)1975 pdf_text = self.__fix_unicode_conversion(pdf_text)1976 if wrap:1977 pdf_text = pdf_text.replace(' \n', ' ')1978 return pdf_text1979 def assert_pdf_text(self, pdf, text, page=None, maxpages=None,1980 password=None, codec='utf-8', wrap=True, nav=False,1981 override=False):1982 """ Asserts text in a PDF file.1983 PDF can be either a URL or a file path on the local file system.1984 @Params1985 pdf - The URL or file path of the PDF file.1986 text - The expected text to verify in the PDF.1987 page - The page number of the PDF to use (optional).1988 If a page number is provided, looks only at that page.1989 (1 is the first page, 2 is the second page, etc.)1990 If no page number is provided, looks at all the pages.1991 maxpages - Instead of providing a page number, you can provide1992 the number of pages to use from the beginning.1993 password - If the PDF is password-protected, enter it here.1994 codec - The compression format for character encoding.1995 (The default codec used by this method is 'utf-8'.)1996 wrap - Replaces ' \n' with ' ' so that individual sentences1997 from a PDF don't get broken up into seperate lines when1998 getting converted into text format.1999 nav - If PDF is a URL, navigates to the URL in the browser first.2000 (Not needed because the PDF will be downloaded anyway.)2001 override - If the PDF file to be downloaded already exists in the2002 downloaded_files/ folder, that PDF will be used2003 instead of downloading it again. """2004 text = self.__fix_unicode_conversion(text)2005 if not codec:2006 codec = 'utf-8'2007 pdf_text = self.get_pdf_text(2008 pdf, page=page, maxpages=maxpages, password=password, codec=codec,2009 wrap=wrap, nav=nav, override=override)2010 if type(page) is int:2011 if text not in pdf_text:2012 raise Exception("PDF [%s] is missing expected text [%s] on "2013 "page [%s]!" % (pdf, text, page))2014 else:2015 if text not in pdf_text:2016 raise Exception("PDF [%s] is missing expected text [%s]!"2017 "" % (pdf, text))2018 return True2019 def create_folder(self, folder):2020 """ Creates a folder of the given name if it doesn't already exist. """2021 if folder.endswith("/"):2022 folder = folder[:-1]2023 if len(folder) < 1:2024 raise Exception("Minimum folder name length = 1.")2025 if not os.path.exists(folder):2026 try:2027 os.makedirs(folder)2028 except Exception:2029 pass2030 def choose_file(self, selector, file_path, by=By.CSS_SELECTOR,2031 timeout=None):2032 """ This method is used to choose a file to upload to a website.2033 It works by populating a file-chooser "input" field of type="file".2034 A relative file_path will get converted into an absolute file_path.2035 Example usage:2036 self.choose_file('input[type="file"]', "my_dir/my_file.txt")2037 """2038 if not timeout:2039 timeout = settings.LARGE_TIMEOUT2040 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2041 timeout = self.__get_new_timeout(timeout)2042 if page_utils.is_xpath_selector(selector):2043 by = By.XPATH2044 abs_path = os.path.abspath(file_path)2045 self.add_text(selector, abs_path, by=by, timeout=timeout)2046 def save_element_as_image_file(self, selector, file_name, folder=None):2047 """ Take a screenshot of an element and save it as an image file.2048 If no folder is specified, will save it to the current folder. """2049 element = self.wait_for_element_visible(selector)2050 element_png = element.screenshot_as_png2051 if len(file_name.split('.')[0]) < 1:2052 raise Exception("Error: file_name length must be > 0.")2053 if not file_name.endswith(".png"):2054 file_name = file_name + ".png"2055 image_file_path = None2056 if folder:2057 if folder.endswith("/"):2058 folder = folder[:-1]2059 if len(folder) > 0:2060 self.create_folder(folder)2061 image_file_path = "%s/%s" % (folder, file_name)2062 if not image_file_path:2063 image_file_path = file_name2064 with open(image_file_path, "wb") as file:2065 file.write(element_png)2066 def download_file(self, file_url, destination_folder=None):2067 """ Downloads the file from the url to the destination folder.2068 If no destination folder is specified, the default one is used.2069 (The default downloads folder = "./downloaded_files") """2070 if not destination_folder:2071 destination_folder = constants.Files.DOWNLOADS_FOLDER2072 if not os.path.exists(destination_folder):2073 os.makedirs(destination_folder)2074 page_utils._download_file_to(file_url, destination_folder)2075 def save_file_as(self, file_url, new_file_name, destination_folder=None):2076 """ Similar to self.download_file(), except that you get to rename the2077 file being downloaded to whatever you want. """2078 if not destination_folder:2079 destination_folder = constants.Files.DOWNLOADS_FOLDER2080 page_utils._download_file_to(2081 file_url, destination_folder, new_file_name)2082 def save_data_as(self, data, file_name, destination_folder=None):2083 """ Saves the data specified to a file of the name specified.2084 If no destination folder is specified, the default one is used.2085 (The default downloads folder = "./downloaded_files") """2086 if not destination_folder:2087 destination_folder = constants.Files.DOWNLOADS_FOLDER2088 page_utils._save_data_as(data, destination_folder, file_name)2089 def get_downloads_folder(self):2090 """ Returns the OS path of the Downloads Folder.2091 (Works with Chrome and Firefox only, for now.) """2092 return download_helper.get_downloads_folder()2093 def get_path_of_downloaded_file(self, file):2094 """ Returns the OS path of the downloaded file. """2095 return os.path.join(self.get_downloads_folder(), file)2096 def is_downloaded_file_present(self, file):2097 """ Checks if the file exists in the Downloads Folder. """2098 return os.path.exists(self.get_path_of_downloaded_file(file))2099 def assert_downloaded_file(self, file):2100 """ Asserts that the file exists in the Downloads Folder. """2101 self.assertTrue(os.path.exists(self.get_path_of_downloaded_file(file)),2102 "File [%s] was not found in the downloads folder [%s]!"2103 "" % (file, self.get_downloads_folder()))2104 if self.demo_mode:2105 messenger_post = ("ASSERT DOWNLOADED FILE: [%s]" % file)2106 js_utils.post_messenger_success_message(2107 self.driver, messenger_post, self.message_duration)2108 def assert_true(self, expr, msg=None):2109 self.assertTrue(expr, msg=msg)2110 if self.demo_mode:2111 messenger_post = ("ASSERT TRUE: {%s}" % expr)2112 js_utils.post_messenger_success_message(2113 self.driver, messenger_post, self.message_duration)2114 def assert_false(self, expr, msg=None):2115 self.assertFalse(expr, msg=msg)2116 if self.demo_mode:2117 messenger_post = ("ASSERT FALSE: {%s}" % expr)2118 js_utils.post_messenger_success_message(2119 self.driver, messenger_post, self.message_duration)2120 def assert_equal(self, first, second, msg=None):2121 self.assertEqual(first, second, msg=msg)2122 if self.demo_mode:2123 messenger_post = ("ASSERT EQUAL: {%s == %s}" % (first, second))2124 js_utils.post_messenger_success_message(2125 self.driver, messenger_post, self.message_duration)2126 def assert_not_equal(self, first, second, msg=None):2127 self.assertNotEqual(first, second, msg=msg)2128 if self.demo_mode:2129 messenger_post = ("ASSERT NOT EQUAL: {%s != %s}" % (first, second))2130 js_utils.post_messenger_success_message(2131 self.driver, messenger_post, self.message_duration)2132 def assert_title(self, title):2133 """ Asserts that the web page title matches the expected title. """2134 expected = title2135 actual = self.get_page_title()2136 self.assertEqual(expected, actual, "Expected page title [%s] "2137 "does not match the actual page title [%s]!"2138 "" % (expected, actual))2139 if self.demo_mode:2140 messenger_post = ("ASSERT TITLE = {%s}" % title)2141 self.__highlight_with_assert_success(messenger_post, "html")2142 def assert_no_js_errors(self):2143 """ Asserts that there are no JavaScript "SEVERE"-level page errors.2144 Works ONLY for Chrome (non-headless) and Chrome-based browsers.2145 Does NOT work on Firefox, Edge, IE, and some other browsers:2146 * See https://github.com/SeleniumHQ/selenium/issues/11612147 Based on the following Stack Overflow solution:2148 * https://stackoverflow.com/a/41150512/7058266 """2149 time.sleep(0.1) # May take a moment for errors to appear after loads.2150 try:2151 browser_logs = self.driver.get_log('browser')2152 except (ValueError, WebDriverException):2153 # If unable to get browser logs, skip the assert and return.2154 return2155 messenger_library = "//cdnjs.cloudflare.com/ajax/libs/messenger"2156 errors = []2157 for entry in browser_logs:2158 if entry['level'] == 'SEVERE':2159 if messenger_library not in entry['message']:2160 # Add errors if not caused by SeleniumBase dependencies2161 errors.append(entry)2162 if len(errors) > 0:2163 current_url = self.get_current_url()2164 raise Exception(2165 "JavaScript errors found on %s => %s" % (current_url, errors))2166 if self.demo_mode and self.browser == 'chrome':2167 messenger_post = ("ASSERT NO JS ERRORS")2168 self.__highlight_with_assert_success(messenger_post, "html")2169 def __activate_html_inspector(self):2170 self.wait_for_ready_state_complete()2171 time.sleep(0.05)2172 js_utils.activate_html_inspector(self.driver)2173 def inspect_html(self):2174 """ Inspects the Page HTML with HTML-Inspector.2175 (https://github.com/philipwalton/html-inspector)2176 (https://cdnjs.com/libraries/html-inspector)2177 Prints the results and also returns them. """2178 self.__activate_html_inspector()2179 script = ("""HTMLInspector.inspect();""")2180 self.execute_script(script)2181 time.sleep(0.1)2182 browser_logs = []2183 try:2184 browser_logs = self.driver.get_log('browser')2185 except (ValueError, WebDriverException):2186 # If unable to get browser logs, skip the assert and return.2187 return("(Unable to Inspect HTML! -> Only works on Chrome!)")2188 messenger_library = "//cdnjs.cloudflare.com/ajax/libs/messenger"2189 url = self.get_current_url()2190 header = '\n* HTML Inspection Results: %s' % url2191 results = [header]2192 row_count = 02193 for entry in browser_logs:2194 message = entry['message']2195 if "0:6053 " in message:2196 message = message.split("0:6053")[1]2197 message = message.replace("\\u003C", "<")2198 if message.startswith(' "') and message.count('"') == 2:2199 message = message.split('"')[1]2200 message = "X - " + message2201 if messenger_library not in message:2202 if message not in results:2203 results.append(message)2204 row_count += 12205 if row_count > 0:2206 results.append('* (See the Console output for details!)')2207 else:2208 results.append('* (No issues detected!)')2209 results = '\n'.join(results)2210 print(results)2211 return(results)2212 def get_google_auth_password(self, totp_key=None):2213 """ Returns a time-based one-time password based on the2214 Google Authenticator password algorithm. Works with Authy.2215 If "totp_key" is not specified, defaults to using the one2216 provided in seleniumbase/config/settings.py2217 Google Auth passwords expire and change at 30-second intervals.2218 If the fetched password expires in the next 1.5 seconds, waits2219 for a new one before returning it (may take up to 1.5 seconds).2220 See https://pyotp.readthedocs.io/en/latest/ for details. """2221 import pyotp2222 if not totp_key:2223 totp_key = settings.TOTP_KEY2224 epoch_interval = time.time() / 30.02225 cycle_lifespan = float(epoch_interval) - int(epoch_interval)2226 if float(cycle_lifespan) > 0.95:2227 # Password expires in the next 1.5 seconds. Wait for a new one.2228 for i in range(30):2229 time.sleep(0.05)2230 epoch_interval = time.time() / 30.02231 cycle_lifespan = float(epoch_interval) - int(epoch_interval)2232 if not float(cycle_lifespan) > 0.95:2233 # The new password cycle has begun2234 break2235 totp = pyotp.TOTP(totp_key)2236 return str(totp.now())2237 def convert_xpath_to_css(self, xpath):2238 return xpath_to_css.convert_xpath_to_css(xpath)2239 def convert_to_css_selector(self, selector, by):2240 """ This method converts a selector to a CSS_SELECTOR.2241 jQuery commands require a CSS_SELECTOR for finding elements.2242 This method should only be used for jQuery/JavaScript actions.2243 Pure JavaScript doesn't support using a:contains("LINK_TEXT"). """2244 if by == By.CSS_SELECTOR:2245 return selector2246 elif by == By.ID:2247 return '#%s' % selector2248 elif by == By.CLASS_NAME:2249 return '.%s' % selector2250 elif by == By.NAME:2251 return '[name="%s"]' % selector2252 elif by == By.TAG_NAME:2253 return selector2254 elif by == By.XPATH:2255 return self.convert_xpath_to_css(selector)2256 elif by == By.LINK_TEXT:2257 return 'a:contains("%s")' % selector2258 elif by == By.PARTIAL_LINK_TEXT:2259 return 'a:contains("%s")' % selector2260 else:2261 raise Exception(2262 "Exception: Could not convert {%s}(by=%s) to CSS_SELECTOR!" % (2263 selector, by))2264 def set_value(self, selector, new_value, by=By.CSS_SELECTOR, timeout=None):2265 """ This method uses JavaScript to update a text field. """2266 if not timeout:2267 timeout = settings.LARGE_TIMEOUT2268 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2269 timeout = self.__get_new_timeout(timeout)2270 if page_utils.is_xpath_selector(selector):2271 by = By.XPATH2272 orginal_selector = selector2273 css_selector = self.convert_to_css_selector(selector, by=by)2274 self.__demo_mode_highlight_if_active(orginal_selector, by)2275 if not self.demo_mode:2276 self.scroll_to(orginal_selector, by=by, timeout=timeout)2277 value = re.escape(new_value)2278 value = self.__escape_quotes_if_needed(value)2279 css_selector = re.escape(css_selector)2280 css_selector = self.__escape_quotes_if_needed(css_selector)2281 script = ("""document.querySelector('%s').value='%s';"""2282 % (css_selector, value))2283 self.execute_script(script)2284 if new_value.endswith('\n'):2285 element = self.wait_for_element_present(2286 orginal_selector, by=by, timeout=timeout)2287 element.send_keys(Keys.RETURN)2288 if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:2289 self.wait_for_ready_state_complete()2290 self.__demo_mode_pause_if_active()2291 def js_update_text(self, selector, new_value, by=By.CSS_SELECTOR,2292 timeout=None):2293 """ Same as self.set_value() """2294 if not timeout:2295 timeout = settings.LARGE_TIMEOUT2296 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2297 timeout = self.__get_new_timeout(timeout)2298 self.set_value(2299 selector, new_value, by=by, timeout=timeout)2300 def jquery_update_text(self, selector, new_value, by=By.CSS_SELECTOR,2301 timeout=None):2302 """ This method uses jQuery to update a text field.2303 If the new_value string ends with the newline character,2304 WebDriver will finish the call, which simulates pressing2305 {Enter/Return} after the text is entered. """2306 if not timeout:2307 timeout = settings.LARGE_TIMEOUT2308 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2309 timeout = self.__get_new_timeout(timeout)2310 if page_utils.is_xpath_selector(selector):2311 by = By.XPATH2312 element = self.wait_for_element_visible(2313 selector, by=by, timeout=timeout)2314 self.__demo_mode_highlight_if_active(selector, by)2315 self.scroll_to(selector, by=by)2316 selector = self.convert_to_css_selector(selector, by=by)2317 selector = self.__make_css_match_first_element_only(selector)2318 selector = self.__escape_quotes_if_needed(selector)2319 new_value = re.escape(new_value)2320 new_value = self.__escape_quotes_if_needed(new_value)2321 update_text_script = """jQuery('%s').val('%s')""" % (2322 selector, new_value)2323 self.safe_execute_script(update_text_script)2324 if new_value.endswith('\n'):2325 element.send_keys('\n')2326 self.__demo_mode_pause_if_active()2327 def set_time_limit(self, time_limit):2328 if time_limit:2329 try:2330 sb_config.time_limit = float(time_limit)2331 except Exception:2332 sb_config.time_limit = None2333 else:2334 sb_config.time_limit = None2335 if sb_config.time_limit and sb_config.time_limit > 0:2336 sb_config.time_limit_ms = int(sb_config.time_limit * 1000.0)2337 self.time_limit = sb_config.time_limit2338 else:2339 self.time_limit = None2340 sb_config.time_limit = None2341 sb_config.time_limit_ms = None2342 def skip_test(self, reason=""):2343 """ Mark the test as Skipped. """2344 self.skipTest(reason)2345 ############2346 def add_css_link(self, css_link):2347 js_utils.add_css_link(self.driver, css_link)2348 def add_js_link(self, js_link):2349 js_utils.add_js_link(self.driver, js_link)2350 def add_css_style(self, css_style):2351 js_utils.add_css_style(self.driver, css_style)2352 def add_js_code_from_link(self, js_link):2353 js_utils.add_js_code_from_link(self.driver, js_link)2354 def add_meta_tag(self, http_equiv=None, content=None):2355 js_utils.add_meta_tag(2356 self.driver, http_equiv=http_equiv, content=content)2357 ############2358 def create_tour(self, name=None, theme=None):2359 """ Creates a tour for a website. By default, the Shepherd JavaScript2360 Library is used with the Shepherd "Light" / "Arrows" theme.2361 @Params2362 name - If creating multiple tours at the same time,2363 use this to select the tour you wish to add steps to.2364 theme - Sets the default theme for the tour.2365 Choose from "light"/"arrows", "dark", "default", "square",2366 and "square-dark". ("arrows" is used if None is selected.)2367 Alternatively, you may use a different JavaScript Library2368 as the theme. Those include "IntroJS", "Bootstrap", and2369 "Hopscotch".2370 """2371 if not name:2372 name = "default"2373 if theme:2374 if theme.lower() == "bootstrap":2375 self.create_bootstrap_tour(name)2376 return2377 elif theme.lower() == "hopscotch":2378 self.create_hopscotch_tour(name)2379 return2380 elif theme.lower() == "intro":2381 self.create_introjs_tour(name)2382 return2383 elif theme.lower() == "introjs":2384 self.create_introjs_tour(name)2385 return2386 elif theme.lower() == "shepherd":2387 self.create_shepherd_tour(name, theme="light")2388 return2389 else:2390 self.create_shepherd_tour(name, theme)2391 else:2392 self.create_shepherd_tour(name, theme="light")2393 def create_shepherd_tour(self, name=None, theme=None):2394 """ Creates a Shepherd JS website tour.2395 @Params2396 name - If creating multiple tours at the same time,2397 use this to select the tour you wish to add steps to.2398 theme - Sets the default theme for the tour.2399 Choose from "light"/"arrows", "dark", "default", "square",2400 and "square-dark". ("light" is used if None is selected.)2401 """2402 shepherd_theme = "shepherd-theme-arrows"2403 if theme:2404 if theme.lower() == "default":2405 shepherd_theme = "shepherd-theme-default"2406 elif theme.lower() == "dark":2407 shepherd_theme = "shepherd-theme-dark"2408 elif theme.lower() == "light":2409 shepherd_theme = "shepherd-theme-arrows"2410 elif theme.lower() == "arrows":2411 shepherd_theme = "shepherd-theme-arrows"2412 elif theme.lower() == "square":2413 shepherd_theme = "shepherd-theme-square"2414 elif theme.lower() == "square-dark":2415 shepherd_theme = "shepherd-theme-square-dark"2416 if not name:2417 name = "default"2418 new_tour = (2419 """2420 // Shepherd Tour2421 var tour = new Shepherd.Tour({2422 defaults: {2423 classes: '%s',2424 scrollTo: true2425 }2426 });2427 var allButtons = {2428 skip: {2429 text: "Skip",2430 action: tour.cancel,2431 classes: 'shepherd-button-secondary tour-button-left'2432 },2433 back: {2434 text: "Back",2435 action: tour.back,2436 classes: 'shepherd-button-secondary'2437 },2438 next: {2439 text: "Next",2440 action: tour.next,2441 classes: 'shepherd-button-primary tour-button-right'2442 },2443 };2444 var firstStepButtons = [allButtons.skip, allButtons.next];2445 var midTourButtons = [allButtons.back, allButtons.next];2446 """ % shepherd_theme)2447 self._tour_steps[name] = []2448 self._tour_steps[name].append(new_tour)2449 def create_bootstrap_tour(self, name=None):2450 """ Creates a Bootstrap tour for a website.2451 @Params2452 name - If creating multiple tours at the same time,2453 use this to select the tour you wish to add steps to.2454 """2455 if not name:2456 name = "default"2457 new_tour = (2458 """2459 // Bootstrap Tour2460 var tour = new Tour({2461 });2462 tour.addSteps([2463 """)2464 self._tour_steps[name] = []2465 self._tour_steps[name].append(new_tour)2466 def create_hopscotch_tour(self, name=None):2467 """ Creates an Hopscotch tour for a website.2468 @Params2469 name - If creating multiple tours at the same time,2470 use this to select the tour you wish to add steps to.2471 """2472 if not name:2473 name = "default"2474 new_tour = (2475 """2476 // Hopscotch Tour2477 var tour = {2478 id: "hopscotch_tour",2479 steps: [2480 """)2481 self._tour_steps[name] = []2482 self._tour_steps[name].append(new_tour)2483 def create_introjs_tour(self, name=None):2484 """ Creates an IntroJS tour for a website.2485 @Params2486 name - If creating multiple tours at the same time,2487 use this to select the tour you wish to add steps to.2488 """2489 if not name:2490 name = "default"2491 new_tour = (2492 """2493 // IntroJS Tour2494 function startIntro(){2495 var intro = introJs();2496 intro.setOptions({2497 steps: [2498 """)2499 self._tour_steps[name] = []2500 self._tour_steps[name].append(new_tour)2501 def add_tour_step(self, message, selector=None, name=None,2502 title=None, theme=None, alignment=None, duration=None):2503 """ Allows the user to add tour steps for a website.2504 @Params2505 message - The message to display.2506 selector - The CSS Selector of the Element to attach to.2507 name - If creating multiple tours at the same time,2508 use this to select the tour you wish to add steps to.2509 title - Additional header text that appears above the message.2510 theme - (NON-Bootstrap Tours ONLY) The styling of the tour step.2511 Choose from "light"/"arrows", "dark", "default", "square",2512 and "square-dark". ("arrows" is used if None is selected.)2513 alignment - Choose from "top", "bottom", "left", and "right".2514 ("top" is the default alignment).2515 duration - (Bootstrap Tours ONLY) The amount of time, in seconds,2516 before automatically advancing to the next tour step.2517 """2518 if not selector:2519 selector = "html"2520 if page_utils.is_xpath_selector(selector):2521 selector = self.convert_to_css_selector(selector, By.XPATH)2522 selector = self.__escape_quotes_if_needed(selector)2523 if not name:2524 name = "default"2525 if name not in self._tour_steps:2526 # By default, will create an IntroJS tour if no tours exist2527 self.create_tour(name=name, theme="introjs")2528 if not title:2529 title = ""2530 title = self.__escape_quotes_if_needed(title)2531 if message:2532 message = self.__escape_quotes_if_needed(message)2533 else:2534 message = ""2535 if not alignment or (2536 alignment not in ["top", "bottom", "left", "right"]):2537 if "Hopscotch" not in self._tour_steps[name][0]:2538 alignment = "top"2539 else:2540 alignment = "bottom"2541 if "Bootstrap" in self._tour_steps[name][0]:2542 self.__add_bootstrap_tour_step(2543 message, selector=selector, name=name, title=title,2544 alignment=alignment, duration=duration)2545 elif "Hopscotch" in self._tour_steps[name][0]:2546 self.__add_hopscotch_tour_step(2547 message, selector=selector, name=name, title=title,2548 alignment=alignment)2549 elif "IntroJS" in self._tour_steps[name][0]:2550 self.__add_introjs_tour_step(2551 message, selector=selector, name=name, title=title,2552 alignment=alignment)2553 else:2554 self.__add_shepherd_tour_step(2555 message, selector=selector, name=name, title=title,2556 theme=theme, alignment=alignment)2557 def __add_shepherd_tour_step(self, message, selector=None, name=None,2558 title=None, theme=None, alignment=None):2559 """ Allows the user to add tour steps for a website.2560 @Params2561 message - The message to display.2562 selector - The CSS Selector of the Element to attach to.2563 name - If creating multiple tours at the same time,2564 use this to select the tour you wish to add steps to.2565 title - Additional header text that appears above the message.2566 theme - (NON-Bootstrap Tours ONLY) The styling of the tour step.2567 Choose from "light"/"arrows", "dark", "default", "square",2568 and "square-dark". ("arrows" is used if None is selected.)2569 alignment - Choose from "top", "bottom", "left", and "right".2570 ("top" is the default alignment).2571 """2572 if theme == "default":2573 shepherd_theme = "shepherd-theme-default"2574 elif theme == "dark":2575 shepherd_theme = "shepherd-theme-dark"2576 elif theme == "light":2577 shepherd_theme = "shepherd-theme-arrows"2578 elif theme == "arrows":2579 shepherd_theme = "shepherd-theme-arrows"2580 elif theme == "square":2581 shepherd_theme = "shepherd-theme-square"2582 elif theme == "square-dark":2583 shepherd_theme = "shepherd-theme-square-dark"2584 else:2585 shepherd_base_theme = re.search(2586 r"[\S\s]+classes: '([\S\s]+)',[\S\s]+",2587 self._tour_steps[name][0]).group(1)2588 shepherd_theme = shepherd_base_theme2589 shepherd_classes = shepherd_theme2590 if selector == "html":2591 shepherd_classes += " shepherd-orphan"2592 buttons = "firstStepButtons"2593 if len(self._tour_steps[name]) > 1:2594 buttons = "midTourButtons"2595 step = ("""2596 tour.addStep('%s', {2597 title: '%s',2598 classes: '%s',2599 text: '%s',2600 attachTo: {element: '%s', on: '%s'},2601 buttons: %s,2602 advanceOn: '.docs-link click'2603 });""" % (2604 name, title, shepherd_classes, message, selector, alignment,2605 buttons))2606 self._tour_steps[name].append(step)2607 def __add_bootstrap_tour_step(self, message, selector=None, name=None,2608 title=None, alignment=None, duration=None):2609 """ Allows the user to add tour steps for a website.2610 @Params2611 message - The message to display.2612 selector - The CSS Selector of the Element to attach to.2613 name - If creating multiple tours at the same time,2614 use this to select the tour you wish to add steps to.2615 title - Additional header text that appears above the message.2616 alignment - Choose from "top", "bottom", "left", and "right".2617 ("top" is the default alignment).2618 duration - (Bootstrap Tours ONLY) The amount of time, in seconds,2619 before automatically advancing to the next tour step.2620 """2621 if selector != "html":2622 selector = self.__make_css_match_first_element_only(selector)2623 element_row = "element: '%s'," % selector2624 else:2625 element_row = ""2626 if not duration:2627 duration = "0"2628 else:2629 duration = str(float(duration) * 1000.0)2630 step = ("""{2631 %s2632 title: '%s',2633 content: '%s',2634 orphan: true,2635 placement: 'auto %s',2636 smartPlacement: true,2637 duration: %s,2638 },""" % (element_row, title, message, alignment, duration))2639 self._tour_steps[name].append(step)2640 def __add_hopscotch_tour_step(self, message, selector=None, name=None,2641 title=None, alignment=None):2642 """ Allows the user to add tour steps for a website.2643 @Params2644 message - The message to display.2645 selector - The CSS Selector of the Element to attach to.2646 name - If creating multiple tours at the same time,2647 use this to select the tour you wish to add steps to.2648 title - Additional header text that appears above the message.2649 alignment - Choose from "top", "bottom", "left", and "right".2650 ("bottom" is the default alignment).2651 """2652 arrow_offset_row = None2653 if not selector or selector == "html":2654 selector = "head"2655 alignment = "bottom"2656 arrow_offset_row = "arrowOffset: '200',"2657 else:2658 arrow_offset_row = ""2659 step = ("""{2660 target: '%s',2661 title: '%s',2662 content: '%s',2663 %s2664 showPrevButton: 'true',2665 scrollDuration: '550',2666 placement: '%s'},2667 """ % (selector, title, message, arrow_offset_row, alignment))2668 self._tour_steps[name].append(step)2669 def __add_introjs_tour_step(self, message, selector=None, name=None,2670 title=None, alignment=None):2671 """ Allows the user to add tour steps for a website.2672 @Params2673 message - The message to display.2674 selector - The CSS Selector of the Element to attach to.2675 name - If creating multiple tours at the same time,2676 use this to select the tour you wish to add steps to.2677 title - Additional header text that appears above the message.2678 alignment - Choose from "top", "bottom", "left", and "right".2679 ("top" is the default alignment).2680 """2681 if selector != "html":2682 element_row = "element: '%s'," % selector2683 else:2684 element_row = ""2685 if title:2686 message = "<center><b>" + title + "</b></center><hr>" + message2687 message = '<font size=\"3\" color=\"#33475B\">' + message + '</font>'2688 step = ("""{%s2689 intro: '%s',2690 position: '%s'},2691 """ % (element_row, message, alignment))2692 self._tour_steps[name].append(step)2693 def play_tour(self, name=None, interval=0):2694 """ Plays a tour on the current website.2695 @Params2696 name - If creating multiple tours at the same time,2697 use this to select the tour you wish to add steps to.2698 interval - The delay time between autoplaying tour steps.2699 If set to 0 (default), the tour is fully manual control.2700 """2701 if self.headless:2702 return # Tours should not run in headless mode.2703 if not name:2704 name = "default"2705 if name not in self._tour_steps:2706 raise Exception("Tour {%s} does not exist!" % name)2707 if "Bootstrap" in self._tour_steps[name][0]:2708 tour_helper.play_bootstrap_tour(2709 self.driver, self._tour_steps, self.browser,2710 self.message_duration, name=name, interval=interval)2711 elif "Hopscotch" in self._tour_steps[name][0]:2712 tour_helper.play_hopscotch_tour(2713 self.driver, self._tour_steps, self.browser,2714 self.message_duration, name=name, interval=interval)2715 elif "IntroJS" in self._tour_steps[name][0]:2716 tour_helper.play_introjs_tour(2717 self.driver, self._tour_steps, self.browser,2718 self.message_duration, name=name, interval=interval)2719 else:2720 # "Shepherd"2721 tour_helper.play_shepherd_tour(2722 self.driver, self._tour_steps,2723 self.message_duration, name=name, interval=interval)2724 def export_tour(self, name=None, filename="my_tour.js", url=None):2725 """ Exports a tour as a JS file.2726 You can call self.export_tour() anywhere where you would2727 normally use self.play_tour() to play a tour.2728 It will include necessary resources as well, such as jQuery.2729 You'll be able to copy the tour directly into the Console of2730 any web browser to play the tour outside of SeleniumBase runs.2731 @Params2732 name - If creating multiple tours at the same time,2733 use this to select the tour you wish to add steps to.2734 filename - The name of the JavaScript file that you wish to2735 save the tour to. """2736 if not url:2737 url = self.get_current_url()2738 tour_helper.export_tour(2739 self._tour_steps, name=name, filename=filename, url=url)2740 def activate_jquery_confirm(self):2741 """ See https://craftpip.github.io/jquery-confirm/ for usage. """2742 js_utils.activate_jquery_confirm(self.driver)2743 def activate_messenger(self):2744 js_utils.activate_messenger(self.driver)2745 def set_messenger_theme(self, theme="default", location="default",2746 max_messages="default"):2747 """ Sets a theme for posting messages.2748 Themes: ["flat", "future", "block", "air", "ice"]2749 Locations: ["top_left", "top_center", "top_right",2750 "bottom_left", "bottom_center", "bottom_right"]2751 max_messages is the limit of concurrent messages to display. """2752 if not theme:2753 theme = "default" # "future"2754 if not location:2755 location = "default" # "bottom_right"2756 if not max_messages:2757 max_messages = "default" # "8"2758 js_utils.set_messenger_theme(2759 self.driver, theme=theme,2760 location=location, max_messages=max_messages)2761 def post_message(self, message, duration=None, pause=True, style="info"):2762 """ Post a message on the screen with Messenger.2763 Arguments:2764 message: The message to display.2765 duration: The time until the message vanishes. (Default: 2.55s)2766 pause: If True, the program waits until the message completes.2767 style: "info", "success", or "error".2768 You can also post messages by using =>2769 self.execute_script('Messenger().post("My Message")')2770 """2771 if not duration:2772 if not self.message_duration:2773 duration = settings.DEFAULT_MESSAGE_DURATION2774 else:2775 duration = self.message_duration2776 js_utils.post_message(2777 self.driver, message, duration, style=style)2778 if pause:2779 duration = float(duration) + 0.152780 time.sleep(float(duration))2781 def post_success_message(self, message, duration=None, pause=True):2782 """ Post a success message on the screen with Messenger.2783 Arguments:2784 message: The success message to display.2785 duration: The time until the message vanishes. (Default: 2.55s)2786 pause: If True, the program waits until the message completes.2787 """2788 if not duration:2789 if not self.message_duration:2790 duration = settings.DEFAULT_MESSAGE_DURATION2791 else:2792 duration = self.message_duration2793 js_utils.post_message(2794 self.driver, message, duration, style="success")2795 if pause:2796 duration = float(duration) + 0.152797 time.sleep(float(duration))2798 def post_error_message(self, message, duration=None, pause=True):2799 """ Post an error message on the screen with Messenger.2800 Arguments:2801 message: The error message to display.2802 duration: The time until the message vanishes. (Default: 2.55s)2803 pause: If True, the program waits until the message completes.2804 """2805 if not duration:2806 if not self.message_duration:2807 duration = settings.DEFAULT_MESSAGE_DURATION2808 else:2809 duration = self.message_duration2810 js_utils.post_message(2811 self.driver, message, duration, style="error")2812 if pause:2813 duration = float(duration) + 0.152814 time.sleep(float(duration))2815 ############2816 def generate_referral(self, start_page, destination_page):2817 """ This method opens the start_page, creates a referral link there,2818 and clicks on that link, which goes to the destination_page.2819 (This generates real traffic for testing analytics software.) """2820 if not page_utils.is_valid_url(destination_page):2821 raise Exception(2822 "Exception: destination_page {%s} is not a valid URL!"2823 % destination_page)2824 if start_page:2825 if not page_utils.is_valid_url(start_page):2826 raise Exception(2827 "Exception: start_page {%s} is not a valid URL! "2828 "(Use an empty string or None to start from current page.)"2829 % start_page)2830 self.open(start_page)2831 time.sleep(0.08)2832 self.wait_for_ready_state_complete()2833 referral_link = ('''<body>'''2834 '''<a class='analytics referral test' href='%s' '''2835 '''style='font-family: Arial,sans-serif; '''2836 '''font-size: 30px; color: #18a2cd'>'''2837 '''Magic Link Button</a></body>''' % destination_page)2838 self.execute_script(2839 '''document.body.outerHTML = \"%s\"''' % referral_link)2840 self.click(2841 "a.analytics.referral.test", timeout=2) # Clicks generated button2842 time.sleep(0.15)2843 try:2844 self.click("html")2845 time.sleep(0.08)2846 except Exception:2847 pass2848 def generate_traffic(self, start_page, destination_page, loops=1):2849 """ Similar to generate_referral(), but can do multiple loops. """2850 for loop in range(loops):2851 self.generate_referral(start_page, destination_page)2852 time.sleep(0.05)2853 def generate_referral_chain(self, pages):2854 """ Use this method to chain the action of creating button links on2855 one website page that will take you to the next page.2856 (When you want to create a referral to a website for traffic2857 generation without increasing the bounce rate, you'll want to visit2858 at least one additional page on that site with a button click.) """2859 if not type(pages) is tuple and not type(pages) is list:2860 raise Exception(2861 "Exception: Expecting a list of website pages for chaining!")2862 if len(pages) < 2:2863 raise Exception(2864 "Exception: At least two website pages required for chaining!")2865 for page in pages:2866 # Find out if any of the web pages are invalid before continuing2867 if not page_utils.is_valid_url(page):2868 raise Exception(2869 "Exception: Website page {%s} is not a valid URL!" % page)2870 for page in pages:2871 self.generate_referral(None, page)2872 def generate_traffic_chain(self, pages, loops=1):2873 """ Similar to generate_referral_chain(), but for multiple loops. """2874 for loop in range(loops):2875 self.generate_referral_chain(pages)2876 time.sleep(0.05)2877 ############2878 def wait_for_element_present(self, selector, by=By.CSS_SELECTOR,2879 timeout=None):2880 """ Waits for an element to appear in the HTML of a page.2881 The element does not need be visible (it may be hidden). """2882 if not timeout:2883 timeout = settings.LARGE_TIMEOUT2884 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2885 timeout = self.__get_new_timeout(timeout)2886 selector, by = self.__recalculate_selector(selector, by)2887 return page_actions.wait_for_element_present(2888 self.driver, selector, by, timeout)2889 def wait_for_element_visible(self, selector, by=By.CSS_SELECTOR,2890 timeout=None):2891 """ Waits for an element to appear in the HTML of a page.2892 The element must be visible (it cannot be hidden). """2893 if not timeout:2894 timeout = settings.LARGE_TIMEOUT2895 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2896 timeout = self.__get_new_timeout(timeout)2897 selector, by = self.__recalculate_selector(selector, by)2898 return page_actions.wait_for_element_visible(2899 self.driver, selector, by, timeout)2900 def wait_for_element(self, selector, by=By.CSS_SELECTOR, timeout=None):2901 """ The shorter version of wait_for_element_visible() """2902 if not timeout:2903 timeout = settings.LARGE_TIMEOUT2904 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2905 timeout = self.__get_new_timeout(timeout)2906 return self.wait_for_element_visible(selector, by=by, timeout=timeout)2907 def get_element(self, selector, by=By.CSS_SELECTOR, timeout=None):2908 """ Same as wait_for_element_present() - returns the element.2909 The element does not need be visible (it may be hidden). """2910 if not timeout:2911 timeout = settings.LARGE_TIMEOUT2912 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2913 timeout = self.__get_new_timeout(timeout)2914 return self.wait_for_element_present(selector, by=by, timeout=timeout)2915 def assert_element_present(self, selector, by=By.CSS_SELECTOR,2916 timeout=None):2917 """ Similar to wait_for_element_present(), but returns nothing.2918 Waits for an element to appear in the HTML of a page.2919 The element does not need be visible (it may be hidden).2920 Returns True if successful. Default timeout = SMALL_TIMEOUT. """2921 if not timeout:2922 timeout = settings.SMALL_TIMEOUT2923 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2924 timeout = self.__get_new_timeout(timeout)2925 self.wait_for_element_present(selector, by=by, timeout=timeout)2926 return True2927 def find_element(self, selector, by=By.CSS_SELECTOR, timeout=None):2928 """ Same as wait_for_element_visible() - returns the element """2929 if not timeout:2930 timeout = settings.LARGE_TIMEOUT2931 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2932 timeout = self.__get_new_timeout(timeout)2933 return self.wait_for_element_visible(selector, by=by, timeout=timeout)2934 def assert_element(self, selector, by=By.CSS_SELECTOR, timeout=None):2935 """ Similar to wait_for_element_visible(), but returns nothing.2936 As above, will raise an exception if nothing can be found.2937 Returns True if successful. Default timeout = SMALL_TIMEOUT. """2938 if not timeout:2939 timeout = settings.SMALL_TIMEOUT2940 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2941 timeout = self.__get_new_timeout(timeout)2942 self.wait_for_element_visible(selector, by=by, timeout=timeout)2943 if self.demo_mode:2944 selector, by = self.__recalculate_selector(selector, by)2945 messenger_post = "ASSERT %s: %s" % (by, selector)2946 self.__highlight_with_assert_success(messenger_post, selector, by)2947 return True2948 def assert_element_visible(self, selector, by=By.CSS_SELECTOR,2949 timeout=None):2950 """ Same as self.assert_element()2951 As above, will raise an exception if nothing can be found. """2952 if not timeout:2953 timeout = settings.SMALL_TIMEOUT2954 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2955 timeout = self.__get_new_timeout(timeout)2956 self.assert_element(selector, by=by, timeout=timeout)2957 return True2958 ############2959 def wait_for_text_visible(self, text, selector="html", by=By.CSS_SELECTOR,2960 timeout=None):2961 if not timeout:2962 timeout = settings.LARGE_TIMEOUT2963 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2964 timeout = self.__get_new_timeout(timeout)2965 selector, by = self.__recalculate_selector(selector, by)2966 return page_actions.wait_for_text_visible(2967 self.driver, text, selector, by, timeout)2968 def wait_for_exact_text_visible(self, text, selector="html",2969 by=By.CSS_SELECTOR,2970 timeout=None):2971 if not timeout:2972 timeout = settings.LARGE_TIMEOUT2973 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2974 timeout = self.__get_new_timeout(timeout)2975 selector, by = self.__recalculate_selector(selector, by)2976 return page_actions.wait_for_exact_text_visible(2977 self.driver, text, selector, by, timeout)2978 def wait_for_text(self, text, selector="html", by=By.CSS_SELECTOR,2979 timeout=None):2980 """ The shorter version of wait_for_text_visible() """2981 if not timeout:2982 timeout = settings.LARGE_TIMEOUT2983 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2984 timeout = self.__get_new_timeout(timeout)2985 return self.wait_for_text_visible(2986 text, selector, by=by, timeout=timeout)2987 def find_text(self, text, selector="html", by=By.CSS_SELECTOR,2988 timeout=None):2989 """ Same as wait_for_text_visible() - returns the element """2990 if not timeout:2991 timeout = settings.LARGE_TIMEOUT2992 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2993 timeout = self.__get_new_timeout(timeout)2994 return self.wait_for_text_visible(2995 text, selector, by=by, timeout=timeout)2996 def assert_text_visible(self, text, selector="html", by=By.CSS_SELECTOR,2997 timeout=None):2998 """ Same as assert_text() """2999 if not timeout:3000 timeout = settings.SMALL_TIMEOUT3001 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3002 timeout = self.__get_new_timeout(timeout)3003 return self.assert_text(text, selector, by=by, timeout=timeout)3004 def assert_text(self, text, selector="html", by=By.CSS_SELECTOR,3005 timeout=None):3006 """ Similar to wait_for_text_visible()3007 Raises an exception if the element or the text is not found.3008 Returns True if successful. Default timeout = SMALL_TIMEOUT. """3009 if not timeout:3010 timeout = settings.SMALL_TIMEOUT3011 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3012 timeout = self.__get_new_timeout(timeout)3013 self.wait_for_text_visible(text, selector, by=by, timeout=timeout)3014 if self.demo_mode:3015 selector, by = self.__recalculate_selector(selector, by)3016 messenger_post = ("ASSERT TEXT {%s} in %s: %s"3017 % (text, by, selector))3018 self.__highlight_with_assert_success(messenger_post, selector, by)3019 return True3020 def assert_exact_text(self, text, selector="html", by=By.CSS_SELECTOR,3021 timeout=None):3022 """ Similar to assert_text(), but the text must be exact, rather than3023 exist as a subset of the full text.3024 (Extra whitespace at the beginning or the end doesn't count.)3025 Raises an exception if the element or the text is not found.3026 Returns True if successful. Default timeout = SMALL_TIMEOUT. """3027 if not timeout:3028 timeout = settings.SMALL_TIMEOUT3029 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3030 timeout = self.__get_new_timeout(timeout)3031 self.wait_for_exact_text_visible(3032 text, selector, by=by, timeout=timeout)3033 if self.demo_mode:3034 selector, by = self.__recalculate_selector(selector, by)3035 messenger_post = ("ASSERT EXACT TEXT {%s} in %s: %s"3036 % (text, by, selector))3037 self.__highlight_with_assert_success(messenger_post, selector, by)3038 return True3039 ############3040 def wait_for_link_text_present(self, link_text, timeout=None):3041 if not timeout:3042 timeout = settings.SMALL_TIMEOUT3043 start_ms = time.time() * 1000.03044 stop_ms = start_ms + (timeout * 1000.0)3045 for x in range(int(timeout * 5)):3046 shared_utils.check_if_time_limit_exceeded()3047 try:3048 if not self.is_link_text_present(link_text):3049 raise Exception(3050 "Link text {%s} was not found!" % link_text)3051 return3052 except Exception:3053 now_ms = time.time() * 1000.03054 if now_ms >= stop_ms:3055 break3056 time.sleep(0.2)3057 raise Exception(3058 "Link text {%s} was not present after %s seconds!" % (3059 link_text, timeout))3060 def wait_for_partial_link_text_present(self, link_text, timeout=None):3061 if not timeout:3062 timeout = settings.SMALL_TIMEOUT3063 start_ms = time.time() * 1000.03064 stop_ms = start_ms + (timeout * 1000.0)3065 for x in range(int(timeout * 5)):3066 shared_utils.check_if_time_limit_exceeded()3067 try:3068 if not self.is_partial_link_text_present(link_text):3069 raise Exception(3070 "Partial Link text {%s} was not found!" % link_text)3071 return3072 except Exception:3073 now_ms = time.time() * 1000.03074 if now_ms >= stop_ms:3075 break3076 time.sleep(0.2)3077 raise Exception(3078 "Partial Link text {%s} was not present after %s seconds!" % (3079 link_text, timeout))3080 def wait_for_link_text_visible(self, link_text, timeout=None):3081 if not timeout:3082 timeout = settings.LARGE_TIMEOUT3083 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3084 timeout = self.__get_new_timeout(timeout)3085 return self.wait_for_element_visible(3086 link_text, by=By.LINK_TEXT, timeout=timeout)3087 def wait_for_link_text(self, link_text, timeout=None):3088 """ The shorter version of wait_for_link_text_visible() """3089 if not timeout:3090 timeout = settings.LARGE_TIMEOUT3091 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3092 timeout = self.__get_new_timeout(timeout)3093 return self.wait_for_link_text_visible(link_text, timeout=timeout)3094 def find_link_text(self, link_text, timeout=None):3095 """ Same as wait_for_link_text_visible() - returns the element """3096 if not timeout:3097 timeout = settings.LARGE_TIMEOUT3098 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3099 timeout = self.__get_new_timeout(timeout)3100 return self.wait_for_link_text_visible(link_text, timeout=timeout)3101 def assert_link_text(self, link_text, timeout=None):3102 """ Similar to wait_for_link_text_visible(), but returns nothing.3103 As above, will raise an exception if nothing can be found.3104 Returns True if successful. Default timeout = SMALL_TIMEOUT. """3105 if not timeout:3106 timeout = settings.SMALL_TIMEOUT3107 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3108 timeout = self.__get_new_timeout(timeout)3109 self.wait_for_link_text_visible(link_text, timeout=timeout)3110 if self.demo_mode:3111 messenger_post = ("ASSERT LINK TEXT {%s}." % link_text)3112 self.__highlight_with_assert_success(3113 messenger_post, link_text, by=By.LINK_TEXT)3114 return True3115 def wait_for_partial_link_text(self, partial_link_text, timeout=None):3116 if not timeout:3117 timeout = settings.LARGE_TIMEOUT3118 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3119 timeout = self.__get_new_timeout(timeout)3120 return self.wait_for_element_visible(3121 partial_link_text, by=By.PARTIAL_LINK_TEXT, timeout=timeout)3122 def find_partial_link_text(self, partial_link_text, timeout=None):3123 """ Same as wait_for_partial_link_text() - returns the element """3124 if not timeout:3125 timeout = settings.LARGE_TIMEOUT3126 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3127 timeout = self.__get_new_timeout(timeout)3128 return self.wait_for_partial_link_text(3129 partial_link_text, timeout=timeout)3130 def assert_partial_link_text(self, partial_link_text, timeout=None):3131 """ Similar to wait_for_partial_link_text(), but returns nothing.3132 As above, will raise an exception if nothing can be found.3133 Returns True if successful. Default timeout = SMALL_TIMEOUT. """3134 if not timeout:3135 timeout = settings.SMALL_TIMEOUT3136 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3137 timeout = self.__get_new_timeout(timeout)3138 self.wait_for_partial_link_text(partial_link_text, timeout=timeout)3139 if self.demo_mode:3140 messenger_post = (3141 "ASSERT PARTIAL LINK TEXT {%s}." % partial_link_text)3142 self.__highlight_with_assert_success(3143 messenger_post, partial_link_text, by=By.PARTIAL_LINK_TEXT)3144 return True3145 ############3146 def wait_for_element_absent(self, selector, by=By.CSS_SELECTOR,3147 timeout=None):3148 """ Waits for an element to no longer appear in the HTML of a page.3149 A hidden element still counts as appearing in the page HTML.3150 If an element with "hidden" status is acceptable,3151 use wait_for_element_not_visible() instead. """3152 if not timeout:3153 timeout = settings.LARGE_TIMEOUT3154 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3155 timeout = self.__get_new_timeout(timeout)3156 if page_utils.is_xpath_selector(selector):3157 by = By.XPATH3158 return page_actions.wait_for_element_absent(3159 self.driver, selector, by, timeout)3160 def assert_element_absent(self, selector, by=By.CSS_SELECTOR,3161 timeout=None):3162 """ Similar to wait_for_element_absent() - returns nothing.3163 As above, will raise an exception if the element stays present.3164 Returns True if successful. Default timeout = SMALL_TIMEOUT. """3165 if not timeout:3166 timeout = settings.SMALL_TIMEOUT3167 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3168 timeout = self.__get_new_timeout(timeout)3169 self.wait_for_element_absent(selector, by=by, timeout=timeout)3170 return True3171 ############3172 def wait_for_element_not_visible(self, selector, by=By.CSS_SELECTOR,3173 timeout=None):3174 """ Waits for an element to no longer be visible on a page.3175 The element can be non-existant in the HTML or hidden on the page3176 to qualify as not visible. """3177 if not timeout:3178 timeout = settings.LARGE_TIMEOUT3179 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3180 timeout = self.__get_new_timeout(timeout)3181 selector, by = self.__recalculate_selector(selector, by)3182 return page_actions.wait_for_element_not_visible(3183 self.driver, selector, by, timeout)3184 def assert_element_not_visible(self, selector, by=By.CSS_SELECTOR,3185 timeout=None):3186 """ Similar to wait_for_element_not_visible() - returns nothing.3187 As above, will raise an exception if the element stays visible.3188 Returns True if successful. Default timeout = SMALL_TIMEOUT. """3189 if not timeout:3190 timeout = settings.SMALL_TIMEOUT3191 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3192 timeout = self.__get_new_timeout(timeout)3193 self.wait_for_element_not_visible(selector, by=by, timeout=timeout)3194 return True3195 ############3196 def wait_for_text_not_visible(self, text, selector="html",3197 by=By.CSS_SELECTOR,3198 timeout=None):3199 if not timeout:3200 timeout = settings.LARGE_TIMEOUT3201 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3202 timeout = self.__get_new_timeout(timeout)3203 selector, by = self.__recalculate_selector(selector, by)3204 return page_actions.wait_for_text_not_visible(3205 self.driver, text, selector, by, timeout)3206 def assert_text_not_visible(self, text, selector="html",3207 by=By.CSS_SELECTOR,3208 timeout=None):3209 """ Similar to wait_for_text_not_visible()3210 Raises an exception if the element or the text is not found.3211 Returns True if successful. Default timeout = SMALL_TIMEOUT. """3212 if not timeout:3213 timeout = settings.SMALL_TIMEOUT3214 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3215 timeout = self.__get_new_timeout(timeout)3216 self.wait_for_text_not_visible(text, selector, by=by, timeout=timeout)3217 ############3218 def wait_for_and_accept_alert(self, timeout=None):3219 if not timeout:3220 timeout = settings.LARGE_TIMEOUT3221 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3222 timeout = self.__get_new_timeout(timeout)3223 return page_actions.wait_for_and_accept_alert(self.driver, timeout)3224 def wait_for_and_dismiss_alert(self, timeout=None):3225 if not timeout:3226 timeout = settings.LARGE_TIMEOUT3227 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3228 timeout = self.__get_new_timeout(timeout)3229 return page_actions.wait_for_and_dismiss_alert(self.driver, timeout)3230 def wait_for_and_switch_to_alert(self, timeout=None):3231 if not timeout:3232 timeout = settings.LARGE_TIMEOUT3233 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3234 timeout = self.__get_new_timeout(timeout)3235 return page_actions.wait_for_and_switch_to_alert(self.driver, timeout)3236 ############3237 def check_window(self, name="default", level=0, baseline=False):3238 """ *** Automated Visual Testing with SeleniumBase ***3239 The first time a test calls self.check_window() for a unique "name"3240 parameter provided, it will set a visual baseline, meaning that it3241 creates a folder, saves the URL to a file, saves the current window3242 screenshot to a file, and creates the following three files3243 with the listed data saved:3244 tags_level1.txt -> HTML tags from the window3245 tags_level2.txt -> HTML tags + attributes from the window3246 tags_level3.txt -> HTML tags + attributes/values from the window3247 Baseline folders are named based on the test name and the name3248 parameter passed to self.check_window(). The same test can store3249 multiple baseline folders.3250 If the baseline is being set/reset, the "level" doesn't matter.3251 After the first run of self.check_window(), it will compare the3252 HTML tags of the latest window to the one from the initial run.3253 Here's how the level system works:3254 * level=0 ->3255 DRY RUN ONLY - Will perform a comparison to the baseline, and3256 print out any differences that are found, but3257 won't fail the test even if differences exist.3258 * level=1 ->3259 HTML tags are compared to tags_level1.txt3260 * level=2 ->3261 HTML tags are compared to tags_level1.txt and3262 HTML tags/attributes are compared to tags_level2.txt3263 * level=3 ->3264 HTML tags are compared to tags_level1.txt and3265 HTML tags + attributes are compared to tags_level2.txt and3266 HTML tags + attributes/values are compared to tags_level3.txt3267 As shown, Level-3 is the most strict, Level-1 is the least strict.3268 If the comparisons from the latest window to the existing baseline3269 don't match, the current test will fail, except for Level-0 tests.3270 You can reset the visual baseline on the command line by using:3271 --visual_baseline3272 As long as "--visual_baseline" is used on the command line while3273 running tests, the self.check_window() method cannot fail because3274 it will rebuild the visual baseline rather than comparing the html3275 tags of the latest run to the existing baseline. If there are any3276 expected layout changes to a website that you're testing, you'll3277 need to reset the baseline to prevent unnecessary failures.3278 self.check_window() will fail with "Page Domain Mismatch Failure"3279 if the page domain doesn't match the domain of the baseline.3280 If you want to use self.check_window() to compare a web page to3281 a later version of itself from within the same test run, you can3282 add the parameter "baseline=True" to the first time you call3283 self.check_window() in a test to use that as the baseline. This3284 only makes sense if you're calling self.check_window() more than3285 once with the same name parameter in the same test.3286 Automated Visual Testing with self.check_window() is not very3287 effective for websites that have dynamic content that changes3288 the layout and structure of web pages. For those, you're much3289 better off using regular SeleniumBase functional testing.3290 Example usage:3291 self.check_window(name="testing", level=0)3292 self.check_window(name="xkcd_home", level=1)3293 self.check_window(name="github_page", level=2)3294 self.check_window(name="wikipedia_page", level=3)3295 """3296 if level == "0":3297 level = 03298 if level == "1":3299 level = 13300 if level == "2":3301 level = 23302 if level == "3":3303 level = 33304 if level != 0 and level != 1 and level != 2 and level != 3:3305 raise Exception('Parameter "level" must be set to 0, 1, 2, or 3!')3306 if self.demo_mode:3307 raise Exception(3308 "WARNING: Using Demo Mode will break layout tests "3309 "that use the check_window() method due to custom "3310 "HTML edits being made on the page!\n"3311 "Please rerun without using Demo Mode!")3312 module = self.__class__.__module__3313 if '.' in module and len(module.split('.')[-1]) > 1:3314 module = module.split('.')[-1]3315 test_id = "%s.%s" % (module, self._testMethodName)3316 if not name or len(name) < 1:3317 name = "default"3318 name = str(name)3319 visual_helper.visual_baseline_folder_setup()3320 baseline_dir = constants.VisualBaseline.STORAGE_FOLDER3321 visual_baseline_path = baseline_dir + "/" + test_id + "/" + name3322 page_url_file = visual_baseline_path + "/page_url.txt"3323 screenshot_file = visual_baseline_path + "/screenshot.png"3324 level_1_file = visual_baseline_path + "/tags_level_1.txt"3325 level_2_file = visual_baseline_path + "/tags_level_2.txt"3326 level_3_file = visual_baseline_path + "/tags_level_3.txt"3327 set_baseline = False3328 if baseline or self.visual_baseline:3329 set_baseline = True3330 if not os.path.exists(visual_baseline_path):3331 set_baseline = True3332 try:3333 os.makedirs(visual_baseline_path)3334 except Exception:3335 pass # Only reachable during multi-threaded test runs3336 if not os.path.exists(page_url_file):3337 set_baseline = True3338 if not os.path.exists(screenshot_file):3339 set_baseline = True3340 if not os.path.exists(level_1_file):3341 set_baseline = True3342 if not os.path.exists(level_2_file):3343 set_baseline = True3344 if not os.path.exists(level_3_file):3345 set_baseline = True3346 page_url = self.get_current_url()3347 soup = self.get_beautiful_soup()3348 html_tags = soup.body.find_all()3349 level_1 = [[tag.name] for tag in html_tags]3350 level_1 = json.loads(json.dumps(level_1)) # Tuples become lists3351 level_2 = [[tag.name, sorted(tag.attrs.keys())] for tag in html_tags]3352 level_2 = json.loads(json.dumps(level_2)) # Tuples become lists3353 level_3 = [[tag.name, sorted(tag.attrs.items())] for tag in html_tags]3354 level_3 = json.loads(json.dumps(level_3)) # Tuples become lists3355 if set_baseline:3356 self.save_screenshot("screenshot.png", visual_baseline_path)3357 out_file = codecs.open(page_url_file, "w+")3358 out_file.writelines(page_url)3359 out_file.close()3360 out_file = codecs.open(level_1_file, "w+")3361 out_file.writelines(json.dumps(level_1))3362 out_file.close()3363 out_file = codecs.open(level_2_file, "w+")3364 out_file.writelines(json.dumps(level_2))3365 out_file.close()3366 out_file = codecs.open(level_3_file, "w+")3367 out_file.writelines(json.dumps(level_3))3368 out_file.close()3369 if not set_baseline:3370 f = open(page_url_file, 'r')3371 page_url_data = f.read().strip()3372 f.close()3373 f = open(level_1_file, 'r')3374 level_1_data = json.loads(f.read())3375 f.close()3376 f = open(level_2_file, 'r')3377 level_2_data = json.loads(f.read())3378 f.close()3379 f = open(level_3_file, 'r')3380 level_3_data = json.loads(f.read())3381 f.close()3382 domain_fail = (3383 "Page Domain Mismatch Failure: "3384 "Current Page Domain doesn't match the Page Domain of the "3385 "Baseline! Can't compare two completely different sites! "3386 "Run with --visual_baseline to reset the baseline!")3387 level_1_failure = (3388 "\n\n*** Exception: <Level 1> Visual Diff Failure:\n"3389 "* HTML tags don't match the baseline!")3390 level_2_failure = (3391 "\n\n*** Exception: <Level 2> Visual Diff Failure:\n"3392 "* HTML tag attributes don't match the baseline!")3393 level_3_failure = (3394 "\n\n*** Exception: <Level 3> Visual Diff Failure:\n"3395 "* HTML tag attribute values don't match the baseline!")3396 page_domain = self.get_domain_url(page_url)3397 page_data_domain = self.get_domain_url(page_url_data)3398 unittest.TestCase.maxDiff = 10003399 if level == 1 or level == 2 or level == 3:3400 self.assertEqual(page_domain, page_data_domain, domain_fail)3401 self.assertEqual(level_1, level_1_data, level_1_failure)3402 unittest.TestCase.maxDiff = None3403 if level == 2 or level == 3:3404 self.assertEqual(level_2, level_2_data, level_2_failure)3405 if level == 3:3406 self.assertEqual(level_3, level_3_data, level_3_failure)3407 if level == 0:3408 try:3409 unittest.TestCase.maxDiff = 10003410 self.assertEqual(3411 page_domain, page_data_domain, domain_fail)3412 self.assertEqual(level_1, level_1_data, level_1_failure)3413 unittest.TestCase.maxDiff = None3414 self.assertEqual(level_2, level_2_data, level_2_failure)3415 self.assertEqual(level_3, level_3_data, level_3_failure)3416 except Exception as e:3417 print(e) # Level-0 Dry Run (Only print the differences)3418 ############3419 def __get_new_timeout(self, timeout):3420 """ When using --timeout_multiplier=#.# """3421 try:3422 timeout_multiplier = float(self.timeout_multiplier)3423 if timeout_multiplier <= 0.5:3424 timeout_multiplier = 0.53425 timeout = int(math.ceil(timeout_multiplier * timeout))3426 return timeout3427 except Exception:3428 # Wrong data type for timeout_multiplier (expecting int or float)3429 return timeout3430 ############3431 def __get_exception_message(self):3432 """ This method extracts the message from an exception if there3433 was an exception that occurred during the test, assuming3434 that the exception was in a try/except block and not thrown. """3435 exception_info = sys.exc_info()[1]3436 if hasattr(exception_info, 'msg'):3437 exc_message = exception_info.msg3438 elif hasattr(exception_info, 'message'):3439 exc_message = exception_info.message3440 else:3441 exc_message = sys.exc_info()3442 return exc_message3443 def __get_improved_exception_message(self):3444 """3445 If Chromedriver is out-of-date, make it clear!3446 Given the high popularity of the following StackOverflow article:3447 https://stackoverflow.com/questions/49162667/unknown-error-3448 call-function-result-missing-value-for-selenium-send-keys-even3449 ... the original error message was not helpful. Tell people directly.3450 (Only expected when using driver.send_keys() with an old Chromedriver.)3451 """3452 exc_message = self.__get_exception_message()3453 maybe_using_old_chromedriver = False3454 if "unknown error: call function result missing" in exc_message:3455 maybe_using_old_chromedriver = True3456 if self.browser == 'chrome' and maybe_using_old_chromedriver:3457 update = ("Your version of ChromeDriver may be out-of-date! "3458 "Please go to "3459 "https://sites.google.com/a/chromium.org/chromedriver/ "3460 "and download the latest version to your system PATH! "3461 "Or use: ``seleniumbase install chromedriver`` . "3462 "Original Exception Message: %s" % exc_message)3463 exc_message = update3464 return exc_message3465 def __add_delayed_assert_failure(self):3466 """ Add a delayed_assert failure into a list for future processing. """3467 current_url = self.driver.current_url3468 message = self.__get_exception_message()3469 self.__delayed_assert_failures.append(3470 "CHECK #%s: (%s)\n %s" % (3471 self.__delayed_assert_count, current_url, message))3472 def delayed_assert_element(self, selector, by=By.CSS_SELECTOR,3473 timeout=None):3474 """ A non-terminating assertion for an element on a page.3475 Failures will be saved until the process_delayed_asserts()3476 method is called from inside a test, likely at the end of it. """3477 if not timeout:3478 timeout = settings.MINI_TIMEOUT3479 if self.timeout_multiplier and timeout == settings.MINI_TIMEOUT:3480 timeout = self.__get_new_timeout(timeout)3481 self.__delayed_assert_count += 13482 try:3483 url = self.get_current_url()3484 if url == self.__last_url_of_delayed_assert:3485 timeout = 13486 else:3487 self.__last_url_of_delayed_assert = url3488 except Exception:3489 pass3490 try:3491 self.wait_for_element_visible(selector, by=by, timeout=timeout)3492 return True3493 except Exception:3494 self.__add_delayed_assert_failure()3495 return False3496 def delayed_assert_text(self, text, selector="html", by=By.CSS_SELECTOR,3497 timeout=None):3498 """ A non-terminating assertion for text from an element on a page.3499 Failures will be saved until the process_delayed_asserts()3500 method is called from inside a test, likely at the end of it. """3501 if not timeout:3502 timeout = settings.MINI_TIMEOUT3503 if self.timeout_multiplier and timeout == settings.MINI_TIMEOUT:3504 timeout = self.__get_new_timeout(timeout)3505 self.__delayed_assert_count += 13506 try:3507 url = self.get_current_url()3508 if url == self.__last_url_of_delayed_assert:3509 timeout = 13510 else:3511 self.__last_url_of_delayed_assert = url3512 except Exception:3513 pass3514 try:3515 self.wait_for_text_visible(text, selector, by=by, timeout=timeout)3516 return True3517 except Exception:3518 self.__add_delayed_assert_failure()3519 return False3520 def process_delayed_asserts(self, print_only=False):3521 """ To be used with any test that uses delayed_asserts, which are3522 non-terminating verifications that only raise exceptions3523 after this method is called.3524 This is useful for pages with multiple elements to be checked when3525 you want to find as many bugs as possible in a single test run3526 before having all the exceptions get raised simultaneously.3527 Might be more useful if this method is called after processing all3528 the delayed asserts on a single html page so that the failure3529 screenshot matches the location of the delayed asserts.3530 If "print_only" is set to True, the exception won't get raised. """3531 if self.__delayed_assert_failures:3532 exception_output = ''3533 exception_output += "\n*** DELAYED ASSERTION FAILURES FOR: "3534 exception_output += "%s\n" % self.id()3535 all_failing_checks = self.__delayed_assert_failures3536 self.__delayed_assert_failures = []3537 for tb in all_failing_checks:3538 exception_output += "%s\n" % tb3539 if print_only:3540 print(exception_output)3541 else:3542 raise Exception(exception_output)3543 ############3544 def __js_click(self, selector, by=By.CSS_SELECTOR):3545 """ Clicks an element using pure JS. Does not use jQuery. """3546 selector, by = self.__recalculate_selector(selector, by)3547 css_selector = self.convert_to_css_selector(selector, by=by)3548 css_selector = re.escape(css_selector)3549 css_selector = self.__escape_quotes_if_needed(css_selector)3550 script = ("""var simulateClick = function (elem) {3551 var evt = new MouseEvent('click', {3552 bubbles: true,3553 cancelable: true,3554 view: window3555 });3556 var canceled = !elem.dispatchEvent(evt);3557 };3558 var someLink = document.querySelector('%s');3559 simulateClick(someLink);"""3560 % css_selector)3561 self.execute_script(script)3562 def __js_click_all(self, selector, by=By.CSS_SELECTOR):3563 """ Clicks all matching elements using pure JS. (No jQuery) """3564 selector, by = self.__recalculate_selector(selector, by)3565 css_selector = self.convert_to_css_selector(selector, by=by)3566 css_selector = re.escape(css_selector)3567 css_selector = self.__escape_quotes_if_needed(css_selector)3568 script = ("""var simulateClick = function (elem) {3569 var evt = new MouseEvent('click', {3570 bubbles: true,3571 cancelable: true,3572 view: window3573 });3574 var canceled = !elem.dispatchEvent(evt);3575 };3576 var $elements = document.querySelectorAll('%s');3577 var index = 0, length = $elements.length;3578 for(; index < length; index++){3579 simulateClick($elements[index]);}"""3580 % css_selector)3581 self.execute_script(script)3582 def __jquery_click(self, selector, by=By.CSS_SELECTOR):3583 """ Clicks an element using jQuery. Different from using pure JS. """3584 selector, by = self.__recalculate_selector(selector, by)3585 self.wait_for_element_present(3586 selector, by=by, timeout=settings.SMALL_TIMEOUT)3587 if self.is_element_visible(selector, by=by):3588 self.__demo_mode_highlight_if_active(selector, by)3589 selector = self.convert_to_css_selector(selector, by=by)3590 selector = self.__make_css_match_first_element_only(selector)3591 click_script = """jQuery('%s')[0].click()""" % selector3592 self.safe_execute_script(click_script)3593 def __get_href_from_link_text(self, link_text, hard_fail=True):3594 href = self.get_link_attribute(link_text, "href", hard_fail)3595 if not href:3596 return None3597 if href.startswith('//'):3598 link = "http:" + href3599 elif href.startswith('/'):3600 url = self.driver.current_url3601 domain_url = self.get_domain_url(url)3602 link = domain_url + href3603 else:3604 link = href3605 return link3606 def __click_dropdown_link_text(self, link_text, link_css):3607 """ When a link may be hidden under a dropdown menu, use this. """3608 soup = self.get_beautiful_soup()3609 drop_down_list = []3610 for item in soup.select('li[class]'):3611 drop_down_list.append(item)3612 csstype = link_css.split('[')[1].split('=')[0]3613 for item in drop_down_list:3614 item_text_list = item.text.split('\n')3615 if link_text in item_text_list and csstype in item.decode():3616 dropdown_css = ""3617 try:3618 for css_class in item['class']:3619 dropdown_css += '.'3620 dropdown_css += css_class3621 except Exception:3622 continue3623 dropdown_css = item.name + dropdown_css3624 matching_dropdowns = self.find_visible_elements(dropdown_css)3625 for dropdown in matching_dropdowns:3626 # The same class names might be used for multiple dropdowns3627 if dropdown.is_displayed():3628 try:3629 try:3630 page_actions.hover_element(3631 self.driver, dropdown)3632 except Exception:3633 # If hovering fails, driver is likely outdated3634 # Time to go directly to the hidden link text3635 self.open(self.__get_href_from_link_text(3636 link_text))3637 return True3638 page_actions.hover_element_and_click(3639 self.driver, dropdown, link_text,3640 click_by=By.LINK_TEXT, timeout=0.12)3641 return True3642 except Exception:3643 pass3644 return False3645 def __get_href_from_partial_link_text(self, link_text, hard_fail=True):3646 href = self.get_partial_link_text_attribute(3647 link_text, "href", hard_fail)3648 if not href:3649 return None3650 if href.startswith('//'):3651 link = "http:" + href3652 elif href.startswith('/'):3653 url = self.driver.current_url3654 domain_url = self.get_domain_url(url)3655 link = domain_url + href3656 else:3657 link = href3658 return link3659 def __click_dropdown_partial_link_text(self, link_text, link_css):3660 """ When a partial link may be hidden under a dropdown, use this. """3661 soup = self.get_beautiful_soup()3662 drop_down_list = []3663 for item in soup.select('li[class]'):3664 drop_down_list.append(item)3665 csstype = link_css.split('[')[1].split('=')[0]3666 for item in drop_down_list:3667 item_text_list = item.text.split('\n')3668 if link_text in item_text_list and csstype in item.decode():3669 dropdown_css = ""3670 try:3671 for css_class in item['class']:3672 dropdown_css += '.'3673 dropdown_css += css_class3674 except Exception:3675 continue3676 dropdown_css = item.name + dropdown_css3677 matching_dropdowns = self.find_visible_elements(dropdown_css)3678 for dropdown in matching_dropdowns:3679 # The same class names might be used for multiple dropdowns3680 if dropdown.is_displayed():3681 try:3682 try:3683 page_actions.hover_element(3684 self.driver, dropdown)3685 except Exception:3686 # If hovering fails, driver is likely outdated3687 # Time to go directly to the hidden link text3688 self.open(3689 self.__get_href_from_partial_link_text(3690 link_text))3691 return True3692 page_actions.hover_element_and_click(3693 self.driver, dropdown, link_text,3694 click_by=By.LINK_TEXT, timeout=0.12)3695 return True3696 except Exception:3697 pass3698 return False3699 def __recalculate_selector(self, selector, by):3700 # Try to determine the type of selector automatically3701 if page_utils.is_xpath_selector(selector):3702 by = By.XPATH3703 if page_utils.is_link_text_selector(selector):3704 selector = page_utils.get_link_text_from_selector(selector)3705 by = By.LINK_TEXT3706 if page_utils.is_partial_link_text_selector(selector):3707 selector = page_utils.get_partial_link_text_from_selector(selector)3708 by = By.PARTIAL_LINK_TEXT3709 if page_utils.is_name_selector(selector):3710 name = page_utils.get_name_from_selector(selector)3711 selector = '[name="%s"]' % name3712 by = By.CSS_SELECTOR3713 if by == By.LINK_TEXT or by == By.PARTIAL_LINK_TEXT:3714 if self.browser == "safari" and selector.lower() != selector:3715 selector = ("""//a[contains(translate(.,"ABCDEFGHIJKLMNOPQR"""3716 """STUVWXYZ","abcdefghijklmnopqrstuvw"""3717 """xyz"),"%s")]""" % selector.lower())3718 by = By.XPATH3719 return (selector, by)3720 def __make_css_match_first_element_only(self, selector):3721 # Only get the first match3722 return page_utils.make_css_match_first_element_only(selector)3723 def __demo_mode_pause_if_active(self, tiny=False):3724 if self.demo_mode:3725 wait_time = settings.DEFAULT_DEMO_MODE_TIMEOUT3726 if self.demo_sleep:3727 wait_time = float(self.demo_sleep)3728 if not tiny:3729 time.sleep(wait_time)3730 else:3731 time.sleep(wait_time / 3.4)3732 elif self.slow_mode:3733 self.__slow_mode_pause_if_active()3734 def __slow_mode_pause_if_active(self):3735 if self.slow_mode:3736 wait_time = settings.DEFAULT_DEMO_MODE_TIMEOUT3737 if self.demo_sleep:3738 wait_time = float(self.demo_sleep)3739 time.sleep(wait_time)3740 def __demo_mode_scroll_if_active(self, selector, by):3741 if self.demo_mode:3742 self.slow_scroll_to(selector, by=by)3743 def __demo_mode_highlight_if_active(self, selector, by):3744 if self.demo_mode:3745 # Includes self.slow_scroll_to(selector, by=by) by default3746 self.highlight(selector, by=by)3747 elif self.slow_mode:3748 # Just do the slow scroll part of the highlight() method3749 selector, by = self.__recalculate_selector(selector, by)3750 element = self.wait_for_element_visible(3751 selector, by=by, timeout=settings.SMALL_TIMEOUT)3752 try:3753 self.__slow_scroll_to_element(element)3754 except (StaleElementReferenceException, ENI_Exception):3755 self.wait_for_ready_state_complete()3756 time.sleep(0.05)3757 element = self.wait_for_element_visible(3758 selector, by=by, timeout=settings.SMALL_TIMEOUT)3759 self.__slow_scroll_to_element(element)3760 def __scroll_to_element(self, element):3761 js_utils.scroll_to_element(self.driver, element)3762 self.__demo_mode_pause_if_active(tiny=True)3763 def __slow_scroll_to_element(self, element):3764 js_utils.slow_scroll_to_element(self.driver, element, self.browser)3765 def __highlight_with_assert_success(3766 self, message, selector, by=By.CSS_SELECTOR):3767 selector, by = self.__recalculate_selector(selector, by)3768 element = self.wait_for_element_visible(3769 selector, by=by, timeout=settings.SMALL_TIMEOUT)3770 try:3771 selector = self.convert_to_css_selector(selector, by=by)3772 except Exception:3773 # Don't highlight if can't convert to CSS_SELECTOR3774 return3775 try:3776 self.__slow_scroll_to_element(element)3777 except (StaleElementReferenceException, ENI_Exception):3778 self.wait_for_ready_state_complete()3779 time.sleep(0.05)3780 element = self.wait_for_element_visible(3781 selector, by=by, timeout=settings.SMALL_TIMEOUT)3782 self.__slow_scroll_to_element(element)3783 o_bs = '' # original_box_shadow3784 try:3785 style = element.get_attribute('style')3786 except (StaleElementReferenceException, ENI_Exception):3787 self.wait_for_ready_state_complete()3788 time.sleep(0.05)3789 element = self.wait_for_element_visible(3790 selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT)3791 style = element.get_attribute('style')3792 if style:3793 if 'box-shadow: ' in style:3794 box_start = style.find('box-shadow: ')3795 box_end = style.find(';', box_start) + 13796 original_box_shadow = style[box_start:box_end]3797 o_bs = original_box_shadow3798 if ":contains" not in selector and ":first" not in selector:3799 selector = re.escape(selector)3800 selector = self.__escape_quotes_if_needed(selector)3801 self.__highlight_with_js_2(message, selector, o_bs)3802 else:3803 selector = self.__make_css_match_first_element_only(selector)3804 selector = re.escape(selector)3805 selector = self.__escape_quotes_if_needed(selector)3806 try:3807 self.__highlight_with_jquery_2(message, selector, o_bs)3808 except Exception:3809 pass # JQuery probably couldn't load. Skip highlighting.3810 time.sleep(0.065)3811 def __highlight_with_js_2(self, message, selector, o_bs):3812 js_utils.highlight_with_js_2(3813 self.driver, message, selector, o_bs, self.message_duration)3814 def __highlight_with_jquery_2(self, message, selector, o_bs):3815 js_utils.highlight_with_jquery_2(3816 self.driver, message, selector, o_bs, self.message_duration)3817 ############3818 # Deprecated Methods (Replace these if they're still in your code!)3819 @decorators.deprecated(3820 "scroll_click() is deprecated. Use self.click() - It scrolls for you!")3821 def scroll_click(self, selector, by=By.CSS_SELECTOR):3822 # DEPRECATED - self.click() now scrolls to the element before clicking.3823 # self.scroll_to(selector, by=by) # Redundant3824 self.click(selector, by=by)3825 @decorators.deprecated(3826 "update_text_value() is deprecated. Use self.update_text() instead!")3827 def update_text_value(self, selector, new_value, by=By.CSS_SELECTOR,3828 timeout=None, retry=False):3829 # DEPRECATED - self.update_text() should be used instead.3830 if not timeout:3831 timeout = settings.LARGE_TIMEOUT3832 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3833 timeout = self.__get_new_timeout(timeout)3834 if page_utils.is_xpath_selector(selector):3835 by = By.XPATH3836 self.update_text(3837 selector, new_value, by=by, timeout=timeout, retry=retry)3838 @decorators.deprecated(3839 "jquery_update_text_value() is deprecated. Use jquery_update_text()")3840 def jquery_update_text_value(self, selector, new_value, by=By.CSS_SELECTOR,3841 timeout=None):3842 # DEPRECATED - self.jquery_update_text() should be used instead.3843 if not timeout:3844 timeout = settings.LARGE_TIMEOUT3845 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3846 timeout = self.__get_new_timeout(timeout)3847 self.jquery_update_text(selector, new_value, by=by, timeout=timeout)3848 @decorators.deprecated(3849 "jq_format() is deprecated. Use re.escape() instead!")3850 def jq_format(self, code):3851 # DEPRECATED - re.escape() already performs the intended action!3852 return js_utils._jq_format(code)3853 ############3854 def setUp(self):3855 """3856 Be careful if a subclass of BaseCase overrides setUp()3857 You'll need to add the following line to the subclass setUp() method:3858 super(SubClassOfBaseCase, self).setUp()3859 """3860 self.masterqa_mode = False3861 self.is_pytest = None3862 try:3863 # This raises an exception if the test is not coming from pytest3864 self.is_pytest = sb_config.is_pytest3865 except Exception:3866 # Not using pytest (probably nosetests)3867 self.is_pytest = False3868 if self.is_pytest:3869 # pytest-specific code3870 test_id = self.__get_test_id()3871 self.browser = sb_config.browser3872 self.data = sb_config.data3873 self.slow_mode = sb_config.slow_mode3874 self.demo_mode = sb_config.demo_mode3875 self.demo_sleep = sb_config.demo_sleep3876 self.highlights = sb_config.highlights3877 self.time_limit = sb_config.time_limit3878 self.environment = sb_config.environment3879 self.env = self.environment # Add a shortened version3880 self.with_selenium = sb_config.with_selenium # Should be True3881 self.headless = sb_config.headless3882 self.headless_active = False3883 self.headed = sb_config.headed3884 self.start_page = sb_config.start_page3885 self.log_path = sb_config.log_path3886 self.with_testing_base = sb_config.with_testing_base3887 self.with_basic_test_info = sb_config.with_basic_test_info3888 self.with_screen_shots = sb_config.with_screen_shots3889 self.with_page_source = sb_config.with_page_source3890 self.with_db_reporting = sb_config.with_db_reporting3891 self.with_s3_logging = sb_config.with_s3_logging3892 self.servername = sb_config.servername3893 self.port = sb_config.port3894 self.proxy_string = sb_config.proxy_string3895 self.user_agent = sb_config.user_agent3896 self.mobile_emulator = sb_config.mobile_emulator3897 self.device_metrics = sb_config.device_metrics3898 self.cap_file = sb_config.cap_file3899 self.settings_file = sb_config.settings_file3900 self.database_env = sb_config.database_env3901 self.message_duration = sb_config.message_duration3902 self.js_checking_on = sb_config.js_checking_on3903 self.ad_block_on = sb_config.ad_block_on3904 self.verify_delay = sb_config.verify_delay3905 self.disable_csp = sb_config.disable_csp3906 self.enable_sync = sb_config.enable_sync3907 self.incognito = sb_config.incognito3908 self.user_data_dir = sb_config.user_data_dir3909 self.extension_zip = sb_config.extension_zip3910 self.extension_dir = sb_config.extension_dir3911 self.maximize_option = sb_config.maximize_option3912 self._reuse_session = sb_config.reuse_session3913 self.save_screenshot_after_test = sb_config.save_screenshot3914 self.visual_baseline = sb_config.visual_baseline3915 self.timeout_multiplier = sb_config.timeout_multiplier3916 self.pytest_html_report = sb_config.pytest_html_report3917 self.report_on = False3918 if self.pytest_html_report:3919 self.report_on = True3920 self.use_grid = False3921 if self.servername != "localhost":3922 # Use Selenium Grid (Use --server=127.0.0.1 for localhost Grid)3923 self.use_grid = True3924 if self.with_db_reporting:3925 from seleniumbase.core.application_manager import (3926 ApplicationManager)3927 from seleniumbase.core.testcase_manager import (3928 ExecutionQueryPayload)3929 import getpass3930 self.execution_guid = str(uuid.uuid4())3931 self.testcase_guid = None3932 self.execution_start_time = 03933 self.case_start_time = 03934 self.application = None3935 self.testcase_manager = None3936 self.error_handled = False3937 self.testcase_manager = TestcaseManager(self.database_env)3938 #3939 exec_payload = ExecutionQueryPayload()3940 exec_payload.execution_start_time = int(time.time() * 1000)3941 self.execution_start_time = exec_payload.execution_start_time3942 exec_payload.guid = self.execution_guid3943 exec_payload.username = getpass.getuser()3944 self.testcase_manager.insert_execution_data(exec_payload)3945 #3946 data_payload = TestcaseDataPayload()3947 self.testcase_guid = str(uuid.uuid4())3948 data_payload.guid = self.testcase_guid3949 data_payload.execution_guid = self.execution_guid3950 if self.with_selenium:3951 data_payload.browser = self.browser3952 else:3953 data_payload.browser = "N/A"3954 data_payload.test_address = test_id3955 application = ApplicationManager.generate_application_string(3956 self._testMethodName)3957 data_payload.env = application.split('.')[0]3958 data_payload.start_time = application.split('.')[1]3959 data_payload.state = constants.State.NOTRUN3960 self.testcase_manager.insert_testcase_data(data_payload)3961 self.case_start_time = int(time.time() * 1000)3962 if self.headless:3963 width = settings.HEADLESS_START_WIDTH3964 height = settings.HEADLESS_START_HEIGHT3965 try:3966 # from pyvirtualdisplay import Display # Skip for own lib3967 from seleniumbase.virtual_display.display import Display3968 self.display = Display(visible=0, size=(width, height))3969 self.display.start()3970 self.headless_active = True3971 except Exception:3972 # pyvirtualdisplay might not be necessary anymore because3973 # Chrome and Firefox now have built-in headless displays3974 pass3975 else:3976 # (Nosetests / Not Pytest)3977 pass # Setup performed in plugins3978 # Verify that SeleniumBase is installed successfully3979 if not hasattr(self, "browser"):3980 raise Exception("""SeleniumBase plugins DID NOT load!\n\n"""3981 """*** Please REINSTALL SeleniumBase using: >\n"""3982 """ >>> "pip install -r requirements.txt"\n"""3983 """ >>> "python setup.py install" """)3984 # Configure the test time limit (if used)3985 self.set_time_limit(self.time_limit)3986 # Set the start time for the test (in ms)3987 sb_config.start_time_ms = int(time.time() * 1000.0)3988 # Parse the settings file3989 if self.settings_file:3990 settings_parser.set_settings(self.settings_file)3991 # Mobile Emulator device metrics: CSS Width, CSS Height, & Pixel-Ratio3992 if self.device_metrics:3993 metrics_string = self.device_metrics3994 metrics_string = metrics_string.replace(' ', '')3995 metrics_list = metrics_string.split(',')3996 exception_string = (3997 'Invalid input for Mobile Emulator device metrics!\n'3998 'Expecting a comma-separated string with three\n'3999 'integer values for Width, Height, and Pixel-Ratio.\n'4000 'Example: --metrics="411,731,3" ')4001 if len(metrics_list) != 3:4002 raise Exception(exception_string)4003 try:4004 self.__device_width = int(metrics_list[0])4005 self.__device_height = int(metrics_list[1])4006 self.__device_pixel_ratio = int(metrics_list[2])4007 self.mobile_emulator = True4008 except Exception:4009 raise Exception(exception_string)4010 if self.mobile_emulator:4011 if not self.user_agent:4012 # Use the Pixel 3 user agent by default if not specified4013 self.user_agent = (4014 "Mozilla/5.0 (Linux; Android 9; Pixel 3 XL) "4015 "AppleWebKit/537.36 (KHTML, like Gecko) "4016 "Chrome/76.0.3809.132 Mobile Safari/537.36")4017 has_url = False4018 if self._reuse_session:4019 if not hasattr(sb_config, 'shared_driver'):4020 sb_config.shared_driver = None4021 if sb_config.shared_driver:4022 try:4023 self._default_driver = sb_config.shared_driver4024 self.driver = sb_config.shared_driver4025 self._drivers_list = [sb_config.shared_driver]4026 url = self.get_current_url()4027 if len(url) > 3:4028 has_url = True4029 except Exception:4030 pass4031 if self._reuse_session and sb_config.shared_driver and has_url:4032 if self.start_page and len(self.start_page) >= 4:4033 if page_utils.is_valid_url(self.start_page):4034 self.open(self.start_page)4035 else:4036 new_start_page = "http://" + self.start_page4037 if page_utils.is_valid_url(new_start_page):4038 self.open(new_start_page)4039 else:4040 if self.get_current_url() != "data:,":4041 self.open("data:,")4042 else:4043 # Launch WebDriver for both Pytest and Nosetests4044 self.driver = self.get_new_driver(browser=self.browser,4045 headless=self.headless,4046 servername=self.servername,4047 port=self.port,4048 proxy=self.proxy_string,4049 agent=self.user_agent,4050 switch_to=True,4051 cap_file=self.cap_file,4052 disable_csp=self.disable_csp,4053 enable_sync=self.enable_sync,4054 incognito=self.incognito,4055 user_data_dir=self.user_data_dir,4056 extension_zip=self.extension_zip,4057 extension_dir=self.extension_dir,4058 is_mobile=self.mobile_emulator,4059 d_width=self.__device_width,4060 d_height=self.__device_height,4061 d_p_r=self.__device_pixel_ratio)4062 self._default_driver = self.driver4063 if self._reuse_session:4064 sb_config.shared_driver = self.driver4065 def __set_last_page_screenshot(self):4066 """ self.__last_page_screenshot is only for pytest html report logs4067 self.__last_page_screenshot_png is for all screenshot log files """4068 if not self.__last_page_screenshot and (4069 not self.__last_page_screenshot_png):4070 try:4071 element = self.driver.find_element_by_tag_name('body')4072 if self.is_pytest and self.report_on:4073 self.__last_page_screenshot_png = (4074 self.driver.get_screenshot_as_png())4075 self.__last_page_screenshot = element.screenshot_as_base644076 else:4077 self.__last_page_screenshot_png = element.screenshot_as_png4078 except Exception:4079 if not self.__last_page_screenshot:4080 if self.is_pytest and self.report_on:4081 try:4082 self.__last_page_screenshot = (4083 self.driver.get_screenshot_as_base64())4084 except Exception:4085 pass4086 if not self.__last_page_screenshot_png:4087 try:4088 self.__last_page_screenshot_png = (4089 self.driver.get_screenshot_as_png())4090 except Exception:4091 pass4092 def __insert_test_result(self, state, err):4093 data_payload = TestcaseDataPayload()4094 data_payload.runtime = int(time.time() * 1000) - self.case_start_time4095 data_payload.guid = self.testcase_guid4096 data_payload.execution_guid = self.execution_guid4097 data_payload.state = state4098 if err:4099 import traceback4100 tb_string = traceback.format_exc()4101 if "Message: " in tb_string:4102 data_payload.message = "Message: " + tb_string.split(4103 "Message: ")[-1]4104 elif "Exception: " in tb_string:4105 data_payload.message = tb_string.split("Exception: ")[-1]4106 elif "Error: " in tb_string:4107 data_payload.message = tb_string.split("Error: ")[-1]4108 else:4109 data_payload.message = "Unknown Error: See Stacktrace"4110 self.testcase_manager.update_testcase_data(data_payload)4111 def __add_pytest_html_extra(self):4112 if not self.__added_pytest_html_extra:4113 try:4114 if self.with_selenium:4115 if not self.__last_page_screenshot:4116 self.__set_last_page_screenshot()4117 if self.report_on:4118 extra_url = {}4119 extra_url['name'] = 'URL'4120 extra_url['format'] = 'url'4121 extra_url['content'] = self.get_current_url()4122 extra_url['mime_type'] = None4123 extra_url['extension'] = None4124 extra_image = {}4125 extra_image['name'] = 'Screenshot'4126 extra_image['format'] = 'image'4127 extra_image['content'] = self.__last_page_screenshot4128 extra_image['mime_type'] = 'image/png'4129 extra_image['extension'] = 'png'4130 self.__added_pytest_html_extra = True4131 self._html_report_extra.append(extra_url)4132 self._html_report_extra.append(extra_image)4133 except Exception:4134 pass4135 def __quit_all_drivers(self):4136 if self._reuse_session and sb_config.shared_driver:4137 if len(self._drivers_list) > 0:4138 sb_config.shared_driver = self._drivers_list[0]4139 self._default_driver = self._drivers_list[0]4140 self.switch_to_default_driver()4141 if len(self._drivers_list) > 1:4142 self._drivers_list = self._drivers_list[1:]4143 else:4144 self._drivers_list = []4145 # Close all open browser windows4146 self._drivers_list.reverse() # Last In, First Out4147 for driver in self._drivers_list:4148 try:4149 driver.quit()4150 except AttributeError:4151 pass4152 except Exception:4153 pass4154 self.driver = None4155 self._default_driver = None4156 self._drivers_list = []4157 def __has_exception(self):4158 has_exception = False4159 if sys.version_info[0] >= 3 and hasattr(self, '_outcome'):4160 if hasattr(self._outcome, 'errors') and self._outcome.errors:4161 has_exception = True4162 else:4163 has_exception = sys.exc_info()[1] is not None4164 return has_exception4165 def __get_test_id(self):4166 test_id = "%s.%s.%s" % (self.__class__.__module__,4167 self.__class__.__name__,4168 self._testMethodName)4169 return test_id4170 def __create_log_path_as_needed(self, test_logpath):4171 if not os.path.exists(test_logpath):4172 try:4173 os.makedirs(test_logpath)4174 except Exception:4175 pass # Only reachable during multi-threaded runs4176 def save_teardown_screenshot(self):4177 """ (Should ONLY be used at the start of custom tearDown() methods.)4178 This method takes a screenshot of the current web page for a4179 failing test (or when running your tests with --save-screenshot).4180 That way your tearDown() method can navigate away from the last4181 page where the test failed, and still get the correct screenshot4182 before performing tearDown() steps on other pages. If this method4183 is not included in your custom tearDown() method, a screenshot4184 will still be taken after the last step of your tearDown(), where4185 you should be calling "super(SubClassOfBaseCase, self).tearDown()"4186 """4187 if self.__has_exception() or self.save_screenshot_after_test:4188 test_id = self.__get_test_id()4189 test_logpath = self.log_path + "/" + test_id4190 self.__create_log_path_as_needed(test_logpath)4191 self.__set_last_page_screenshot()4192 if self.is_pytest:4193 self.__add_pytest_html_extra()4194 def tearDown(self):4195 """4196 Be careful if a subclass of BaseCase overrides setUp()4197 You'll need to add the following line to the subclass's tearDown():4198 super(SubClassOfBaseCase, self).tearDown()4199 """4200 self.__slow_mode_pause_if_active()4201 has_exception = self.__has_exception()4202 if self.__delayed_assert_failures:4203 print(4204 "\nWhen using self.delayed_assert_*() methods in your tests, "4205 "remember to call self.process_delayed_asserts() afterwards. "4206 "Now calling in tearDown()...\nFailures Detected:")4207 if not has_exception:4208 self.process_delayed_asserts()4209 else:4210 self.process_delayed_asserts(print_only=True)4211 if self.is_pytest:4212 # pytest-specific code4213 test_id = self.__get_test_id()4214 try:...

Full Screen

Full Screen

baseCase.py

Source:baseCase.py Github

copy

Full Screen

...150 sleep(wait_time)151 else:152 sleep(wait_time / 3.4)153 elif self.slow_mode:154 self.__slow_mode_pause_if_active()155 def __slow_mode_pause_if_active(self):156 if self.slow_mode:157 wait_time = settings.DEFAULT_DEMO_MODE_TIMEOUT158 if self.demo_sleep:159 wait_time = float(self.demo_sleep)160 sleep(wait_time)161 def get_current_url(self):162 """163 获取当前url地址164 :return:165 """166 self.__check_scope()167 current_url = self.driver.current_url168 if "%" in current_url and sys.version_info[0] >= 3:169 try:...

Full Screen

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run SeleniumBase automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful