Best Python code snippet using SeleniumBase
base_case.py
Source:base_case.py
...90 self._chart_series_count = {}91 self._tour_steps = {}92 def open(self, url):93 """ Navigates the current browser window to the specified page. """94 self.__check_scope()95 if type(url) is str:96 url = url.strip() # Remove leading and trailing whitespace97 if (type(url) is not str) or not self.__looks_like_a_page_url(url):98 # url should start with one of the following:99 # "http:", "https:", "://", "data:", "file:",100 # "about:", "chrome:", "opera:", or "edge:".101 msg = 'Did you forget to prefix your URL with "http:" or "https:"?'102 raise Exception('Invalid URL: "%s"\n%s' % (url, msg))103 self.__last_page_load_url = None104 js_utils.clear_out_console_logs(self.driver)105 if url.startswith("://"):106 # Convert URLs such as "://google.com" into "https://google.com"107 url = "https" + url108 if self.browser == "safari" and url.startswith("data:"):109 url = re.escape(url)110 url = self.__escape_quotes_if_needed(url)111 self.execute_script("window.location.href='%s';" % url)112 else:113 self.driver.get(url)114 if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:115 self.wait_for_ready_state_complete()116 self.__demo_mode_pause_if_active()117 def get(self, url):118 """ If url looks like a page URL, opens the URL in the web browser.119 Otherwise, returns self.get_element(URL_AS_A_SELECTOR)120 Examples:121 self.get("https://seleniumbase.io") # Navigates to the URL122 self.get("input.class") # Finds and returns the WebElement123 """124 self.__check_scope()125 if self.__looks_like_a_page_url(url):126 self.open(url)127 else:128 return self.get_element(url) # url is treated like a selector129 def click(self, selector, by=By.CSS_SELECTOR, timeout=None, delay=0):130 self.__check_scope()131 if not timeout:132 timeout = settings.SMALL_TIMEOUT133 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:134 timeout = self.__get_new_timeout(timeout)135 original_selector = selector136 original_by = by137 selector, by = self.__recalculate_selector(selector, by)138 if page_utils.is_link_text_selector(selector) or by == By.LINK_TEXT:139 if not self.is_link_text_visible(selector):140 # Handle a special case of links hidden in dropdowns141 self.click_link_text(selector, timeout=timeout)142 return143 if page_utils.is_partial_link_text_selector(selector) or (144 by == By.PARTIAL_LINK_TEXT):145 if not self.is_partial_link_text_visible(selector):146 # Handle a special case of partial links hidden in dropdowns147 self.click_partial_link_text(selector, timeout=timeout)148 return149 element = page_actions.wait_for_element_visible(150 self.driver, selector, by, timeout=timeout)151 self.__demo_mode_highlight_if_active(original_selector, original_by)152 if not self.demo_mode and not self.slow_mode:153 self.__scroll_to_element(element, selector, by)154 pre_action_url = self.driver.current_url155 if delay and delay > 0:156 time.sleep(delay)157 try:158 if self.browser == "ie" and by == By.LINK_TEXT:159 # An issue with clicking Link Text on IE means using jquery160 self.__jquery_click(selector, by=by)161 elif self.browser == "safari":162 if by == By.LINK_TEXT:163 self.__jquery_click(selector, by=by)164 else:165 self.__js_click(selector, by=by)166 else:167 # Normal click168 element.click()169 except (StaleElementReferenceException, ENI_Exception):170 self.wait_for_ready_state_complete()171 time.sleep(0.16)172 element = page_actions.wait_for_element_visible(173 self.driver, selector, by, timeout=timeout)174 if self.browser == "safari":175 if by == By.LINK_TEXT:176 self.__jquery_click(selector, by=by)177 else:178 self.__js_click(selector, by=by)179 else:180 element.click()181 except (WebDriverException, MoveTargetOutOfBoundsException):182 self.wait_for_ready_state_complete()183 try:184 self.__js_click(selector, by=by)185 except Exception:186 try:187 self.__jquery_click(selector, by=by)188 except Exception:189 # One more attempt to click on the element190 element = page_actions.wait_for_element_visible(191 self.driver, selector, by, timeout=timeout)192 element.click()193 if settings.WAIT_FOR_RSC_ON_CLICKS:194 self.wait_for_ready_state_complete()195 if self.demo_mode:196 if self.driver.current_url != pre_action_url:197 self.__demo_mode_pause_if_active()198 else:199 self.__demo_mode_pause_if_active(tiny=True)200 elif self.slow_mode:201 self.__slow_mode_pause_if_active()202 def slow_click(self, selector, by=By.CSS_SELECTOR, timeout=None):203 """ Similar to click(), but pauses for a brief moment before clicking.204 When used in combination with setting the user-agent, you can often205 bypass bot-detection by tricking websites into thinking that you're206 not a bot. (Useful on websites that block web automation tools.)207 To set the user-agent, use: ``--agent=AGENT``.208 Here's an example message from GitHub's bot-blocker:209 ``You have triggered an abuse detection mechanism...`` """210 self.__check_scope()211 if not timeout:212 timeout = settings.SMALL_TIMEOUT213 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:214 timeout = self.__get_new_timeout(timeout)215 if not self.demo_mode and not self.slow_mode:216 self.click(selector, by=by, timeout=timeout, delay=1.05)217 elif self.slow_mode:218 self.click(selector, by=by, timeout=timeout, delay=0.65)219 else:220 # Demo Mode already includes a small delay221 self.click(selector, by=by, timeout=timeout, delay=0.25)222 def double_click(self, selector, by=By.CSS_SELECTOR, timeout=None):223 from selenium.webdriver.common.action_chains import ActionChains224 self.__check_scope()225 if not timeout:226 timeout = settings.SMALL_TIMEOUT227 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:228 timeout = self.__get_new_timeout(timeout)229 original_selector = selector230 original_by = by231 selector, by = self.__recalculate_selector(selector, by)232 element = page_actions.wait_for_element_visible(233 self.driver, selector, by, timeout=timeout)234 self.__demo_mode_highlight_if_active(original_selector, original_by)235 if not self.demo_mode and not self.slow_mode:236 self.__scroll_to_element(element, selector, by)237 self.wait_for_ready_state_complete()238 # Find the element one more time in case scrolling hid it239 element = page_actions.wait_for_element_visible(240 self.driver, selector, by, timeout=timeout)241 pre_action_url = self.driver.current_url242 try:243 if self.browser == "safari":244 # Jump to the "except" block where the other script should work245 raise Exception("This Exception will be caught.")246 actions = ActionChains(self.driver)247 actions.double_click(element).perform()248 except Exception:249 css_selector = self.convert_to_css_selector(selector, by=by)250 css_selector = re.escape(css_selector) # Add "\\" to special chars251 css_selector = self.__escape_quotes_if_needed(css_selector)252 double_click_script = (253 """var targetElement1 = document.querySelector('%s');254 var clickEvent1 = document.createEvent('MouseEvents');255 clickEvent1.initEvent('dblclick', true, true);256 targetElement1.dispatchEvent(clickEvent1);""" % css_selector)257 if ":contains\\(" not in css_selector:258 self.execute_script(double_click_script)259 else:260 double_click_script = (261 """jQuery('%s').dblclick();""" % css_selector)262 self.safe_execute_script(double_click_script)263 if settings.WAIT_FOR_RSC_ON_CLICKS:264 self.wait_for_ready_state_complete()265 if self.demo_mode:266 if self.driver.current_url != pre_action_url:267 self.__demo_mode_pause_if_active()268 else:269 self.__demo_mode_pause_if_active(tiny=True)270 elif self.slow_mode:271 self.__slow_mode_pause_if_active()272 def click_chain(self, selectors_list, by=By.CSS_SELECTOR,273 timeout=None, spacing=0):274 """ This method clicks on a list of elements in succession.275 'spacing' is the amount of time to wait between clicks. (sec) """276 self.__check_scope()277 if not timeout:278 timeout = settings.SMALL_TIMEOUT279 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:280 timeout = self.__get_new_timeout(timeout)281 for selector in selectors_list:282 self.click(selector, by=by, timeout=timeout)283 if spacing > 0:284 time.sleep(spacing)285 def update_text(self, selector, text, by=By.CSS_SELECTOR,286 timeout=None, retry=False):287 """ This method updates an element's text field with new text.288 Has multiple parts:289 * Waits for the element to be visible.290 * Waits for the element to be interactive.291 * Clears the text field.292 * Types in the new text.293 * Hits Enter/Submit (if the text ends in "\n").294 @Params295 selector - the selector of the text field296 text - the new text to type into the text field297 by - the type of selector to search by (Default: CSS Selector)298 timeout - how long to wait for the selector to be visible299 retry - if True, use JS if the Selenium text update fails300 """301 self.__check_scope()302 if not timeout:303 timeout = settings.LARGE_TIMEOUT304 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:305 timeout = self.__get_new_timeout(timeout)306 selector, by = self.__recalculate_selector(selector, by)307 element = self.wait_for_element_visible(308 selector, by=by, timeout=timeout)309 self.__demo_mode_highlight_if_active(selector, by)310 if not self.demo_mode and not self.slow_mode:311 self.__scroll_to_element(element, selector, by)312 try:313 element.clear() # May need https://stackoverflow.com/a/50691625314 backspaces = Keys.BACK_SPACE * 42 # Is the answer to everything315 element.send_keys(backspaces) # In case autocomplete keeps text316 except (StaleElementReferenceException, ENI_Exception):317 self.wait_for_ready_state_complete()318 time.sleep(0.16)319 element = self.wait_for_element_visible(320 selector, by=by, timeout=timeout)321 try:322 element.clear()323 except Exception:324 pass # Clearing the text field first might not be necessary325 except Exception:326 pass # Clearing the text field first might not be necessary327 self.__demo_mode_pause_if_active(tiny=True)328 pre_action_url = self.driver.current_url329 if type(text) is int or type(text) is float:330 text = str(text)331 try:332 if not text.endswith('\n'):333 element.send_keys(text)334 if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:335 self.wait_for_ready_state_complete()336 else:337 element.send_keys(text[:-1])338 element.send_keys(Keys.RETURN)339 if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:340 self.wait_for_ready_state_complete()341 except (StaleElementReferenceException, ENI_Exception):342 self.wait_for_ready_state_complete()343 time.sleep(0.16)344 element = self.wait_for_element_visible(345 selector, by=by, timeout=timeout)346 element.clear()347 if not text.endswith('\n'):348 element.send_keys(text)349 else:350 element.send_keys(text[:-1])351 element.send_keys(Keys.RETURN)352 if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:353 self.wait_for_ready_state_complete()354 except Exception:355 exc_message = self.__get_improved_exception_message()356 raise Exception(exc_message)357 if (retry and element.get_attribute('value') != text and (358 not text.endswith('\n'))):359 logging.debug('update_text() is falling back to JavaScript!')360 self.set_value(selector, text, by=by)361 if self.demo_mode:362 if self.driver.current_url != pre_action_url:363 self.__demo_mode_pause_if_active()364 else:365 self.__demo_mode_pause_if_active(tiny=True)366 elif self.slow_mode:367 self.__slow_mode_pause_if_active()368 def add_text(self, selector, text, by=By.CSS_SELECTOR, timeout=None):369 """ The more-reliable version of driver.send_keys()370 Similar to update_text(), but won't clear the text field first. """371 self.__check_scope()372 if not timeout:373 timeout = settings.LARGE_TIMEOUT374 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:375 timeout = self.__get_new_timeout(timeout)376 selector, by = self.__recalculate_selector(selector, by)377 element = self.wait_for_element_visible(378 selector, by=by, timeout=timeout)379 self.__demo_mode_highlight_if_active(selector, by)380 if not self.demo_mode and not self.slow_mode:381 self.__scroll_to_element(element, selector, by)382 pre_action_url = self.driver.current_url383 if type(text) is int or type(text) is float:384 text = str(text)385 try:386 if not text.endswith('\n'):387 element.send_keys(text)388 else:389 element.send_keys(text[:-1])390 element.send_keys(Keys.RETURN)391 if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:392 self.wait_for_ready_state_complete()393 except (StaleElementReferenceException, ENI_Exception):394 self.wait_for_ready_state_complete()395 time.sleep(0.16)396 element = self.wait_for_element_visible(397 selector, by=by, timeout=timeout)398 if not text.endswith('\n'):399 element.send_keys(text)400 else:401 element.send_keys(text[:-1])402 element.send_keys(Keys.RETURN)403 if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:404 self.wait_for_ready_state_complete()405 except Exception:406 exc_message = self.__get_improved_exception_message()407 raise Exception(exc_message)408 if self.demo_mode:409 if self.driver.current_url != pre_action_url:410 self.__demo_mode_pause_if_active()411 else:412 self.__demo_mode_pause_if_active(tiny=True)413 elif self.slow_mode:414 self.__slow_mode_pause_if_active()415 def type(self, selector, text, by=By.CSS_SELECTOR,416 timeout=None, retry=False):417 """ Same as self.update_text()418 This method updates an element's text field with new text.419 Has multiple parts:420 * Waits for the element to be visible.421 * Waits for the element to be interactive.422 * Clears the text field.423 * Types in the new text.424 * Hits Enter/Submit (if the text ends in "\n").425 @Params426 selector - the selector of the text field427 text - the new text to type into the text field428 by - the type of selector to search by (Default: CSS Selector)429 timeout - how long to wait for the selector to be visible430 retry - if True, use JS if the Selenium text update fails431 DO NOT confuse self.type() with Python type()! They are different!432 """433 self.__check_scope()434 if not timeout:435 timeout = settings.LARGE_TIMEOUT436 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:437 timeout = self.__get_new_timeout(timeout)438 selector, by = self.__recalculate_selector(selector, by)439 self.update_text(selector, text, by=by, timeout=timeout, retry=retry)440 def submit(self, selector, by=By.CSS_SELECTOR):441 """ Alternative to self.driver.find_element_by_*(SELECTOR).submit() """442 self.__check_scope()443 selector, by = self.__recalculate_selector(selector, by)444 element = self.wait_for_element_visible(445 selector, by=by, timeout=settings.SMALL_TIMEOUT)446 element.submit()447 self.__demo_mode_pause_if_active()448 def clear(self, selector, by=By.CSS_SELECTOR, timeout=None):449 """ This method clears an element's text field.450 A clear() is already included with most methods that type text,451 such as self.type(), self.update_text(), etc.452 Does not use Demo Mode highlights, mainly because we expect453 that some users will be calling an unnecessary clear() before454 calling a method that already includes clear() as part of it.455 In case websites trigger an autofill after clearing a field,456 add backspaces to make sure autofill doesn't undo the clear.457 @Params458 selector - the selector of the text field459 by - the type of selector to search by (Default: CSS Selector)460 timeout - how long to wait for the selector to be visible461 """462 self.__check_scope()463 if not timeout:464 timeout = settings.LARGE_TIMEOUT465 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:466 timeout = self.__get_new_timeout(timeout)467 selector, by = self.__recalculate_selector(selector, by)468 element = self.wait_for_element_visible(469 selector, by=by, timeout=timeout)470 self.scroll_to(selector, by=by, timeout=timeout)471 try:472 element.clear()473 backspaces = Keys.BACK_SPACE * 42 # Autofill Defense474 element.send_keys(backspaces)475 except (StaleElementReferenceException, ENI_Exception):476 self.wait_for_ready_state_complete()477 time.sleep(0.16)478 element = self.wait_for_element_visible(479 selector, by=by, timeout=timeout)480 element.clear()481 try:482 backspaces = Keys.BACK_SPACE * 42 # Autofill Defense483 element.send_keys(backspaces)484 except Exception:485 pass486 except Exception:487 element.clear()488 def refresh_page(self):489 self.__check_scope()490 self.__last_page_load_url = None491 js_utils.clear_out_console_logs(self.driver)492 self.driver.refresh()493 self.wait_for_ready_state_complete()494 def refresh(self):495 """ The shorter version of self.refresh_page() """496 self.refresh_page()497 def get_current_url(self):498 self.__check_scope()499 current_url = self.driver.current_url500 if "%" in current_url and sys.version_info[0] >= 3:501 try:502 from urllib.parse import unquote503 current_url = unquote(current_url, errors='strict')504 except Exception:505 pass506 return current_url507 def get_page_source(self):508 self.wait_for_ready_state_complete()509 return self.driver.page_source510 def get_page_title(self):511 self.wait_for_ready_state_complete()512 self.wait_for_element_present("title", timeout=settings.SMALL_TIMEOUT)513 time.sleep(0.03)514 return self.driver.title515 def get_title(self):516 """ The shorter version of self.get_page_title() """517 return self.get_page_title()518 def get_user_agent(self):519 self.__check_scope()520 user_agent = self.driver.execute_script("return navigator.userAgent;")521 return user_agent522 def get_locale_code(self):523 self.__check_scope()524 locale_code = self.driver.execute_script(525 "return navigator.language || navigator.languages[0];")526 return locale_code527 def go_back(self):528 self.__check_scope()529 self.__last_page_load_url = None530 self.driver.back()531 if self.browser == "safari":532 self.wait_for_ready_state_complete()533 self.driver.refresh()534 self.wait_for_ready_state_complete()535 self.__demo_mode_pause_if_active()536 def go_forward(self):537 self.__check_scope()538 self.__last_page_load_url = None539 self.driver.forward()540 self.wait_for_ready_state_complete()541 self.__demo_mode_pause_if_active()542 def is_element_present(self, selector, by=By.CSS_SELECTOR):543 self.wait_for_ready_state_complete()544 selector, by = self.__recalculate_selector(selector, by)545 return page_actions.is_element_present(self.driver, selector, by)546 def is_element_visible(self, selector, by=By.CSS_SELECTOR):547 self.wait_for_ready_state_complete()548 selector, by = self.__recalculate_selector(selector, by)549 return page_actions.is_element_visible(self.driver, selector, by)550 def is_text_visible(self, text, selector="html", by=By.CSS_SELECTOR):551 self.wait_for_ready_state_complete()552 time.sleep(0.01)553 selector, by = self.__recalculate_selector(selector, by)554 return page_actions.is_text_visible(self.driver, text, selector, by)555 def is_link_text_visible(self, link_text):556 self.wait_for_ready_state_complete()557 time.sleep(0.01)558 return page_actions.is_element_visible(self.driver, link_text,559 by=By.LINK_TEXT)560 def is_partial_link_text_visible(self, partial_link_text):561 self.wait_for_ready_state_complete()562 time.sleep(0.01)563 return page_actions.is_element_visible(self.driver, partial_link_text,564 by=By.PARTIAL_LINK_TEXT)565 def is_link_text_present(self, link_text):566 """ Returns True if the link text appears in the HTML of the page.567 The element doesn't need to be visible,568 such as elements hidden inside a dropdown selection. """569 self.wait_for_ready_state_complete()570 soup = self.get_beautiful_soup()571 html_links = soup.find_all('a')572 for html_link in html_links:573 if html_link.text.strip() == link_text.strip():574 return True575 return False576 def is_partial_link_text_present(self, link_text):577 """ Returns True if the partial link appears in the HTML of the page.578 The element doesn't need to be visible,579 such as elements hidden inside a dropdown selection. """580 self.wait_for_ready_state_complete()581 soup = self.get_beautiful_soup()582 html_links = soup.find_all('a')583 for html_link in html_links:584 if link_text.strip() in html_link.text.strip():585 return True586 return False587 def get_link_attribute(self, link_text, attribute, hard_fail=True):588 """ Finds a link by link text and then returns the attribute's value.589 If the link text or attribute cannot be found, an exception will590 get raised if hard_fail is True (otherwise None is returned). """591 self.wait_for_ready_state_complete()592 soup = self.get_beautiful_soup()593 html_links = soup.find_all('a')594 for html_link in html_links:595 if html_link.text.strip() == link_text.strip():596 if html_link.has_attr(attribute):597 attribute_value = html_link.get(attribute)598 return attribute_value599 if hard_fail:600 raise Exception(601 'Unable to find attribute {%s} from link text {%s}!'602 % (attribute, link_text))603 else:604 return None605 if hard_fail:606 raise Exception("Link text {%s} was not found!" % link_text)607 else:608 return None609 def get_link_text_attribute(self, link_text, attribute, hard_fail=True):610 """ Same as self.get_link_attribute()611 Finds a link by link text and then returns the attribute's value.612 If the link text or attribute cannot be found, an exception will613 get raised if hard_fail is True (otherwise None is returned). """614 return self.get_link_attribute(link_text, attribute, hard_fail)615 def get_partial_link_text_attribute(self, link_text, attribute,616 hard_fail=True):617 """ Finds a link by partial link text and then returns the attribute's618 value. If the partial link text or attribute cannot be found, an619 exception will get raised if hard_fail is True (otherwise None620 is returned). """621 self.wait_for_ready_state_complete()622 soup = self.get_beautiful_soup()623 html_links = soup.find_all('a')624 for html_link in html_links:625 if link_text.strip() in html_link.text.strip():626 if html_link.has_attr(attribute):627 attribute_value = html_link.get(attribute)628 return attribute_value629 if hard_fail:630 raise Exception(631 'Unable to find attribute {%s} from '632 'partial link text {%s}!'633 % (attribute, link_text))634 else:635 return None636 if hard_fail:637 raise Exception(638 "Partial Link text {%s} was not found!" % link_text)639 else:640 return None641 def click_link_text(self, link_text, timeout=None):642 """ This method clicks link text on a page """643 # If using phantomjs, might need to extract and open the link directly644 self.__check_scope()645 if not timeout:646 timeout = settings.SMALL_TIMEOUT647 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:648 timeout = self.__get_new_timeout(timeout)649 if self.browser == "phantomjs":650 if self.is_link_text_visible(link_text):651 element = self.wait_for_link_text_visible(652 link_text, timeout=timeout)653 element.click()654 return655 self.open(self.__get_href_from_link_text(link_text))656 return657 if self.browser == "safari":658 if self.demo_mode:659 self.wait_for_link_text_present(link_text, timeout=timeout)660 try:661 self.__jquery_slow_scroll_to(link_text, by=By.LINK_TEXT)662 except Exception:663 element = self.wait_for_link_text_visible(664 link_text, timeout=timeout)665 self.__slow_scroll_to_element(element)666 o_bs = '' # original_box_shadow667 loops = settings.HIGHLIGHTS668 selector = self.convert_to_css_selector(669 link_text, by=By.LINK_TEXT)670 selector = self.__make_css_match_first_element_only(selector)671 try:672 selector = re.escape(selector)673 selector = self.__escape_quotes_if_needed(selector)674 self.__highlight_with_jquery(selector, loops, o_bs)675 except Exception:676 pass # JQuery probably couldn't load. Skip highlighting.677 self.__jquery_click(link_text, by=By.LINK_TEXT)678 return679 if not self.is_link_text_present(link_text):680 self.wait_for_link_text_present(link_text, timeout=timeout)681 pre_action_url = self.get_current_url()682 try:683 element = self.wait_for_link_text_visible(684 link_text, timeout=0.2)685 self.__demo_mode_highlight_if_active(link_text, by=By.LINK_TEXT)686 try:687 element.click()688 except (StaleElementReferenceException, ENI_Exception):689 self.wait_for_ready_state_complete()690 time.sleep(0.16)691 element = self.wait_for_link_text_visible(692 link_text, timeout=timeout)693 element.click()694 except Exception:695 found_css = False696 text_id = self.get_link_attribute(link_text, "id", False)697 if text_id:698 link_css = '[id="%s"]' % link_text699 found_css = True700 if not found_css:701 href = self.__get_href_from_link_text(link_text, False)702 if href:703 if href.startswith('/') or page_utils.is_valid_url(href):704 link_css = '[href="%s"]' % href705 found_css = True706 if not found_css:707 ngclick = self.get_link_attribute(link_text, "ng-click", False)708 if ngclick:709 link_css = '[ng-click="%s"]' % ngclick710 found_css = True711 if not found_css:712 onclick = self.get_link_attribute(link_text, "onclick", False)713 if onclick:714 link_css = '[onclick="%s"]' % onclick715 found_css = True716 success = False717 if found_css:718 if self.is_element_visible(link_css):719 self.click(link_css)720 success = True721 else:722 # The link text might be hidden under a dropdown menu723 success = self.__click_dropdown_link_text(724 link_text, link_css)725 if not success:726 element = self.wait_for_link_text_visible(727 link_text, timeout=settings.MINI_TIMEOUT)728 element.click()729 if settings.WAIT_FOR_RSC_ON_CLICKS:730 self.wait_for_ready_state_complete()731 if self.demo_mode:732 if self.driver.current_url != pre_action_url:733 self.__demo_mode_pause_if_active()734 else:735 self.__demo_mode_pause_if_active(tiny=True)736 elif self.slow_mode:737 self.__slow_mode_pause_if_active()738 def click_partial_link_text(self, partial_link_text, timeout=None):739 """ This method clicks the partial link text on a page. """740 # If using phantomjs, might need to extract and open the link directly741 self.__check_scope()742 if not timeout:743 timeout = settings.SMALL_TIMEOUT744 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:745 timeout = self.__get_new_timeout(timeout)746 if self.browser == "phantomjs":747 if self.is_partial_link_text_visible(partial_link_text):748 element = self.wait_for_partial_link_text(partial_link_text)749 element.click()750 return751 soup = self.get_beautiful_soup()752 html_links = soup.fetch('a')753 for html_link in html_links:754 if partial_link_text in html_link.text:755 for html_attribute in html_link.attrs:756 if html_attribute[0] == 'href':757 href = html_attribute[1]758 if href.startswith('//'):759 link = "http:" + href760 elif href.startswith('/'):761 url = self.driver.current_url762 domain_url = self.get_domain_url(url)763 link = domain_url + href764 else:765 link = href766 self.open(link)767 return768 raise Exception(769 'Could not parse link from partial link_text '770 '{%s}' % partial_link_text)771 raise Exception(772 "Partial link text {%s} was not found!" % partial_link_text)773 if not self.is_partial_link_text_present(partial_link_text):774 self.wait_for_partial_link_text_present(775 partial_link_text, timeout=timeout)776 pre_action_url = self.get_current_url()777 try:778 element = self.wait_for_partial_link_text(779 partial_link_text, timeout=0.2)780 self.__demo_mode_highlight_if_active(781 partial_link_text, by=By.LINK_TEXT)782 try:783 element.click()784 except (StaleElementReferenceException, ENI_Exception):785 self.wait_for_ready_state_complete()786 time.sleep(0.16)787 element = self.wait_for_partial_link_text(788 partial_link_text, timeout=timeout)789 element.click()790 except Exception:791 found_css = False792 text_id = self.get_partial_link_text_attribute(793 partial_link_text, "id", False)794 if text_id:795 link_css = '[id="%s"]' % partial_link_text796 found_css = True797 if not found_css:798 href = self.__get_href_from_partial_link_text(799 partial_link_text, False)800 if href:801 if href.startswith('/') or page_utils.is_valid_url(href):802 link_css = '[href="%s"]' % href803 found_css = True804 if not found_css:805 ngclick = self.get_partial_link_text_attribute(806 partial_link_text, "ng-click", False)807 if ngclick:808 link_css = '[ng-click="%s"]' % ngclick809 found_css = True810 if not found_css:811 onclick = self.get_partial_link_text_attribute(812 partial_link_text, "onclick", False)813 if onclick:814 link_css = '[onclick="%s"]' % onclick815 found_css = True816 success = False817 if found_css:818 if self.is_element_visible(link_css):819 self.click(link_css)820 success = True821 else:822 # The link text might be hidden under a dropdown menu823 success = self.__click_dropdown_partial_link_text(824 partial_link_text, link_css)825 if not success:826 element = self.wait_for_partial_link_text(827 partial_link_text, timeout=settings.MINI_TIMEOUT)828 element.click()829 if settings.WAIT_FOR_RSC_ON_CLICKS:830 self.wait_for_ready_state_complete()831 if self.demo_mode:832 if self.driver.current_url != pre_action_url:833 self.__demo_mode_pause_if_active()834 else:835 self.__demo_mode_pause_if_active(tiny=True)836 elif self.slow_mode:837 self.__slow_mode_pause_if_active()838 def get_text(self, selector, by=By.CSS_SELECTOR, timeout=None):839 self.__check_scope()840 if not timeout:841 timeout = settings.SMALL_TIMEOUT842 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:843 timeout = self.__get_new_timeout(timeout)844 selector, by = self.__recalculate_selector(selector, by)845 self.wait_for_ready_state_complete()846 time.sleep(0.01)847 element = page_actions.wait_for_element_visible(848 self.driver, selector, by, timeout)849 try:850 element_text = element.text851 except (StaleElementReferenceException, ENI_Exception):852 self.wait_for_ready_state_complete()853 time.sleep(0.14)854 element = page_actions.wait_for_element_visible(855 self.driver, selector, by, timeout)856 element_text = element.text857 return element_text858 def get_attribute(self, selector, attribute, by=By.CSS_SELECTOR,859 timeout=None, hard_fail=True):860 """ This method uses JavaScript to get the value of an attribute. """861 self.__check_scope()862 if not timeout:863 timeout = settings.SMALL_TIMEOUT864 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:865 timeout = self.__get_new_timeout(timeout)866 selector, by = self.__recalculate_selector(selector, by)867 self.wait_for_ready_state_complete()868 time.sleep(0.01)869 element = page_actions.wait_for_element_present(870 self.driver, selector, by, timeout)871 try:872 attribute_value = element.get_attribute(attribute)873 except (StaleElementReferenceException, ENI_Exception):874 self.wait_for_ready_state_complete()875 time.sleep(0.14)876 element = page_actions.wait_for_element_present(877 self.driver, selector, by, timeout)878 attribute_value = element.get_attribute(attribute)879 if attribute_value is not None:880 return attribute_value881 else:882 if hard_fail:883 raise Exception("Element {%s} has no attribute {%s}!" % (884 selector, attribute))885 else:886 return None887 def set_attribute(self, selector, attribute, value, by=By.CSS_SELECTOR,888 timeout=None):889 """ This method uses JavaScript to set/update an attribute.890 Only the first matching selector from querySelector() is used. """891 self.__check_scope()892 if not timeout:893 timeout = settings.SMALL_TIMEOUT894 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:895 timeout = self.__get_new_timeout(timeout)896 selector, by = self.__recalculate_selector(selector, by)897 if self.is_element_visible(selector, by=by):898 try:899 self.scroll_to(selector, by=by, timeout=timeout)900 except Exception:901 pass902 attribute = re.escape(attribute)903 attribute = self.__escape_quotes_if_needed(attribute)904 value = re.escape(value)905 value = self.__escape_quotes_if_needed(value)906 css_selector = self.convert_to_css_selector(selector, by=by)907 css_selector = re.escape(css_selector) # Add "\\" to special chars908 css_selector = self.__escape_quotes_if_needed(css_selector)909 script = ("""document.querySelector('%s').setAttribute('%s','%s');"""910 % (css_selector, attribute, value))911 self.execute_script(script)912 def set_attributes(self, selector, attribute, value, by=By.CSS_SELECTOR):913 """ This method uses JavaScript to set/update a common attribute.914 All matching selectors from querySelectorAll() are used.915 Example => (Make all links on a website redirect to Google):916 self.set_attributes("a", "href", "https://google.com") """917 self.__check_scope()918 selector, by = self.__recalculate_selector(selector, by)919 attribute = re.escape(attribute)920 attribute = self.__escape_quotes_if_needed(attribute)921 value = re.escape(value)922 value = self.__escape_quotes_if_needed(value)923 css_selector = self.convert_to_css_selector(selector, by=by)924 css_selector = re.escape(css_selector) # Add "\\" to special chars925 css_selector = self.__escape_quotes_if_needed(css_selector)926 script = ("""var $elements = document.querySelectorAll('%s');927 var index = 0, length = $elements.length;928 for(; index < length; index++){929 $elements[index].setAttribute('%s','%s');}"""930 % (css_selector, attribute, value))931 try:932 self.execute_script(script)933 except Exception:934 pass935 def set_attribute_all(self, selector, attribute, value,936 by=By.CSS_SELECTOR):937 """ Same as set_attributes(), but using querySelectorAll naming scheme.938 This method uses JavaScript to set/update a common attribute.939 All matching selectors from querySelectorAll() are used.940 Example => (Make all links on a website redirect to Google):941 self.set_attribute_all("a", "href", "https://google.com") """942 self.set_attributes(selector, attribute, value, by=by)943 def remove_attribute(self, selector, attribute, by=By.CSS_SELECTOR,944 timeout=None):945 """ This method uses JavaScript to remove an attribute.946 Only the first matching selector from querySelector() is used. """947 self.__check_scope()948 if not timeout:949 timeout = settings.SMALL_TIMEOUT950 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:951 timeout = self.__get_new_timeout(timeout)952 selector, by = self.__recalculate_selector(selector, by)953 if self.is_element_visible(selector, by=by):954 try:955 self.scroll_to(selector, by=by, timeout=timeout)956 except Exception:957 pass958 attribute = re.escape(attribute)959 attribute = self.__escape_quotes_if_needed(attribute)960 css_selector = self.convert_to_css_selector(selector, by=by)961 css_selector = re.escape(css_selector) # Add "\\" to special chars962 css_selector = self.__escape_quotes_if_needed(css_selector)963 script = ("""document.querySelector('%s').removeAttribute('%s');"""964 % (css_selector, attribute))965 self.execute_script(script)966 def remove_attributes(self, selector, attribute, by=By.CSS_SELECTOR):967 """ This method uses JavaScript to remove a common attribute.968 All matching selectors from querySelectorAll() are used. """969 self.__check_scope()970 selector, by = self.__recalculate_selector(selector, by)971 attribute = re.escape(attribute)972 attribute = self.__escape_quotes_if_needed(attribute)973 css_selector = self.convert_to_css_selector(selector, by=by)974 css_selector = re.escape(css_selector) # Add "\\" to special chars975 css_selector = self.__escape_quotes_if_needed(css_selector)976 script = ("""var $elements = document.querySelectorAll('%s');977 var index = 0, length = $elements.length;978 for(; index < length; index++){979 $elements[index].removeAttribute('%s');}"""980 % (css_selector, attribute))981 try:982 self.execute_script(script)983 except Exception:984 pass985 def get_property_value(self, selector, property, by=By.CSS_SELECTOR,986 timeout=None):987 """ Returns the property value of a page element's computed style.988 Example:989 opacity = self.get_property_value("html body a", "opacity")990 self.assertTrue(float(opacity) > 0, "Element not visible!") """991 self.__check_scope()992 if not timeout:993 timeout = settings.SMALL_TIMEOUT994 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:995 timeout = self.__get_new_timeout(timeout)996 selector, by = self.__recalculate_selector(selector, by)997 self.wait_for_ready_state_complete()998 page_actions.wait_for_element_present(999 self.driver, selector, by, timeout)1000 try:1001 selector = self.convert_to_css_selector(selector, by=by)1002 except Exception:1003 # Don't run action if can't convert to CSS_Selector for JavaScript1004 raise Exception(1005 "Exception: Could not convert {%s}(by=%s) to CSS_SELECTOR!" % (1006 selector, by))1007 selector = re.escape(selector)1008 selector = self.__escape_quotes_if_needed(selector)1009 script = ("""var $elm = document.querySelector('%s');1010 $val = window.getComputedStyle($elm).getPropertyValue('%s');1011 return $val;"""1012 % (selector, property))1013 value = self.execute_script(script)1014 if value is not None:1015 return value1016 else:1017 return "" # Return an empty string if the property doesn't exist1018 def get_image_url(self, selector, by=By.CSS_SELECTOR, timeout=None):1019 """ Extracts the URL from an image element on the page. """1020 self.__check_scope()1021 if not timeout:1022 timeout = settings.SMALL_TIMEOUT1023 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1024 timeout = self.__get_new_timeout(timeout)1025 return self.get_attribute(selector,1026 attribute='src', by=by, timeout=timeout)1027 def find_elements(self, selector, by=By.CSS_SELECTOR, limit=0):1028 """ Returns a list of matching WebElements.1029 Elements could be either hidden or visible on the page.1030 If "limit" is set and > 0, will only return that many elements. """1031 selector, by = self.__recalculate_selector(selector, by)1032 self.wait_for_ready_state_complete()1033 time.sleep(0.05)1034 elements = self.driver.find_elements(by=by, value=selector)1035 if limit and limit > 0 and len(elements) > limit:1036 elements = elements[:limit]1037 return elements1038 def find_visible_elements(self, selector, by=By.CSS_SELECTOR, limit=0):1039 """ Returns a list of matching WebElements that are visible.1040 If "limit" is set and > 0, will only return that many elements. """1041 selector, by = self.__recalculate_selector(selector, by)1042 self.wait_for_ready_state_complete()1043 time.sleep(0.05)1044 v_elems = page_actions.find_visible_elements(self.driver, selector, by)1045 if limit and limit > 0 and len(v_elems) > limit:1046 v_elems = v_elems[:limit]1047 return v_elems1048 def click_visible_elements(1049 self, selector, by=By.CSS_SELECTOR, limit=0, timeout=None):1050 """ Finds all matching page elements and clicks visible ones in order.1051 If a click reloads or opens a new page, the clicking will stop.1052 If no matching elements appear, an Exception will be raised.1053 If "limit" is set and > 0, will only click that many elements.1054 Also clicks elements that become visible from previous clicks.1055 Works best for actions such as clicking all checkboxes on a page.1056 Example: self.click_visible_elements('input[type="checkbox"]') """1057 self.__check_scope()1058 if not timeout:1059 timeout = settings.SMALL_TIMEOUT1060 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1061 timeout = self.__get_new_timeout(timeout)1062 selector, by = self.__recalculate_selector(selector, by)1063 self.wait_for_element_present(selector, by=by, timeout=timeout)1064 elements = self.find_elements(selector, by=by)1065 if self.browser == "safari":1066 if not limit:1067 limit = 01068 num_elements = len(elements)1069 if num_elements == 0:1070 raise Exception(1071 "No matching elements found for selector {%s}!" % selector)1072 elif num_elements < limit or limit == 0:1073 limit = num_elements1074 selector, by = self.__recalculate_selector(selector, by)1075 css_selector = self.convert_to_css_selector(selector, by=by)1076 last_css_chunk = css_selector.split(' ')[-1]1077 if ":" in last_css_chunk:1078 self.__js_click_all(css_selector)1079 self.wait_for_ready_state_complete()1080 return1081 else:1082 for i in range(1, limit+1):1083 new_selector = css_selector + ":nth-of-type(%s)" % str(i)1084 if self.is_element_visible(new_selector):1085 self.__js_click(new_selector)1086 self.wait_for_ready_state_complete()1087 return1088 click_count = 01089 for element in elements:1090 if limit and limit > 0 and click_count >= limit:1091 return1092 try:1093 if element.is_displayed():1094 self.__scroll_to_element(element)1095 element.click()1096 click_count += 11097 self.wait_for_ready_state_complete()1098 except ECI_Exception:1099 continue # ElementClickInterceptedException (Overlay likely)1100 except (StaleElementReferenceException, ENI_Exception):1101 self.wait_for_ready_state_complete()1102 time.sleep(0.12)1103 try:1104 if element.is_displayed():1105 self.__scroll_to_element(element)1106 element.click()1107 click_count += 11108 self.wait_for_ready_state_complete()1109 except (StaleElementReferenceException, ENI_Exception):1110 return # Probably on new page / Elements are all stale1111 def click_nth_visible_element(1112 self, selector, number, by=By.CSS_SELECTOR, timeout=None):1113 """ Finds all matching page elements and clicks the nth visible one.1114 Example: self.click_nth_visible_element('[type="checkbox"]', 5)1115 (Clicks the 5th visible checkbox on the page.) """1116 self.__check_scope()1117 if not timeout:1118 timeout = settings.SMALL_TIMEOUT1119 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1120 timeout = self.__get_new_timeout(timeout)1121 selector, by = self.__recalculate_selector(selector, by)1122 self.wait_for_element_present(selector, by=by, timeout=timeout)1123 elements = self.find_visible_elements(selector, by=by)1124 if len(elements) < number:1125 raise Exception("Not enough matching {%s} elements of type {%s} to"1126 " click number %s!" % (selector, by, number))1127 number = number - 11128 if number < 0:1129 number = 01130 element = elements[number]1131 self.wait_for_ready_state_complete()1132 try:1133 self.__scroll_to_element(element)1134 element.click()1135 except (StaleElementReferenceException, ENI_Exception):1136 self.wait_for_ready_state_complete()1137 time.sleep(0.12)1138 self.__scroll_to_element(element)1139 element.click()1140 def click_if_visible(self, selector, by=By.CSS_SELECTOR):1141 """ If the page selector exists and is visible, clicks on the element.1142 This method only clicks on the first matching element found.1143 (Use click_visible_elements() to click all matching elements.) """1144 self.wait_for_ready_state_complete()1145 if self.is_element_visible(selector, by=by):1146 self.click(selector, by=by)1147 def is_checked(self, selector, by=By.CSS_SELECTOR, timeout=None):1148 """ Determines if a checkbox or a radio button element is checked.1149 Returns True if the element is checked.1150 Returns False if the element is not checked.1151 If the element is not present on the page, raises an exception.1152 If the element is not a checkbox or radio, raises an exception. """1153 self.__check_scope()1154 if not timeout:1155 timeout = settings.SMALL_TIMEOUT1156 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1157 timeout = self.__get_new_timeout(timeout)1158 selector, by = self.__recalculate_selector(selector, by)1159 kind = self.get_attribute(selector, "type", by=by, timeout=timeout)1160 if kind != "checkbox" and kind != "radio":1161 raise Exception("Expecting a checkbox or a radio button element!")1162 is_checked = self.get_attribute(1163 selector, "checked", by=by, timeout=timeout, hard_fail=False)1164 if is_checked:1165 return True1166 else: # (NoneType)1167 return False1168 def is_selected(self, selector, by=By.CSS_SELECTOR, timeout=None):1169 """ Same as is_checked() """1170 return self.is_checked(selector, by=by, timeout=timeout)1171 def check_if_unchecked(self, selector, by=By.CSS_SELECTOR):1172 """ If a checkbox or radio button is not checked, will check it. """1173 self.__check_scope()1174 selector, by = self.__recalculate_selector(selector, by)1175 if not self.is_checked(selector, by=by):1176 if self.is_element_visible(selector, by=by):1177 self.click(selector, by=by)1178 else:1179 selector = self.convert_to_css_selector(selector, by=by)1180 self.js_click(selector, by=By.CSS_SELECTOR)1181 def select_if_unselected(self, selector, by=By.CSS_SELECTOR):1182 """ Same as check_if_unchecked() """1183 self.check_if_unchecked(selector, by=by)1184 def uncheck_if_checked(self, selector, by=By.CSS_SELECTOR):1185 """ If a checkbox is checked, will uncheck it. """1186 self.__check_scope()1187 selector, by = self.__recalculate_selector(selector, by)1188 if self.is_checked(selector, by=by):1189 if self.is_element_visible(selector, by=by):1190 self.click(selector, by=by)1191 else:1192 selector = self.convert_to_css_selector(selector, by=by)1193 self.js_click(selector, by=By.CSS_SELECTOR)1194 def unselect_if_selected(self, selector, by=By.CSS_SELECTOR):1195 """ Same as uncheck_if_checked() """1196 self.uncheck_if_checked(selector, by=by)1197 def is_element_in_an_iframe(self, selector, by=By.CSS_SELECTOR):1198 """ Returns True if the selector's element is located in an iframe.1199 Otherwise returns False. """1200 self.__check_scope()1201 selector, by = self.__recalculate_selector(selector, by)1202 if self.is_element_present(selector, by=by):1203 return False1204 soup = self.get_beautiful_soup()1205 iframe_list = soup.select('iframe')1206 for iframe in iframe_list:1207 iframe_identifier = None1208 if iframe.has_attr('name') and len(iframe['name']) > 0:1209 iframe_identifier = iframe['name']1210 elif iframe.has_attr('id') and len(iframe['id']) > 0:1211 iframe_identifier = iframe['id']1212 elif iframe.has_attr('class') and len(iframe['class']) > 0:1213 iframe_class = " ".join(iframe["class"])1214 iframe_identifier = '[class="%s"]' % iframe_class1215 else:1216 continue1217 self.switch_to_frame(iframe_identifier)1218 if self.is_element_present(selector, by=by):1219 self.switch_to_default_content()1220 return True1221 self.switch_to_default_content()1222 return False1223 def switch_to_frame_of_element(self, selector, by=By.CSS_SELECTOR):1224 """ Set driver control to the iframe containing element (assuming the1225 element is in a single-nested iframe) and returns the iframe name.1226 If element is not in an iframe, returns None, and nothing happens.1227 May not work if multiple iframes are nested within each other. """1228 self.__check_scope()1229 selector, by = self.__recalculate_selector(selector, by)1230 if self.is_element_present(selector, by=by):1231 return None1232 soup = self.get_beautiful_soup()1233 iframe_list = soup.select('iframe')1234 for iframe in iframe_list:1235 iframe_identifier = None1236 if iframe.has_attr('name') and len(iframe['name']) > 0:1237 iframe_identifier = iframe['name']1238 elif iframe.has_attr('id') and len(iframe['id']) > 0:1239 iframe_identifier = iframe['id']1240 elif iframe.has_attr('class') and len(iframe['class']) > 0:1241 iframe_class = " ".join(iframe["class"])1242 iframe_identifier = '[class="%s"]' % iframe_class1243 else:1244 continue1245 try:1246 self.switch_to_frame(iframe_identifier, timeout=1)1247 if self.is_element_present(selector, by=by):1248 return iframe_identifier1249 except Exception:1250 pass1251 self.switch_to_default_content()1252 try:1253 self.switch_to_frame(selector, timeout=1)1254 return selector1255 except Exception:1256 if self.is_element_present(selector, by=by):1257 return ""1258 raise Exception("Could not switch to iframe containing "1259 "element {%s}!" % selector)1260 def hover_on_element(self, selector, by=By.CSS_SELECTOR):1261 self.__check_scope()1262 original_selector = selector1263 original_by = by1264 selector, by = self.__recalculate_selector(selector, by)1265 if page_utils.is_xpath_selector(selector):1266 selector = self.convert_to_css_selector(selector, By.XPATH)1267 by = By.CSS_SELECTOR1268 self.wait_for_element_visible(1269 selector, by=by, timeout=settings.SMALL_TIMEOUT)1270 self.__demo_mode_highlight_if_active(original_selector, original_by)1271 self.scroll_to(selector, by=by)1272 time.sleep(0.05) # Settle down from scrolling before hovering1273 if self.browser != "chrome":1274 return page_actions.hover_on_element(self.driver, selector)1275 # Using Chrome1276 # (Pure hover actions won't work on early chromedriver versions)1277 try:1278 return page_actions.hover_on_element(self.driver, selector)1279 except WebDriverException as e:1280 driver_capabilities = self.driver.__dict__["capabilities"]1281 if "version" in driver_capabilities:1282 chrome_version = driver_capabilities["version"]1283 else:1284 chrome_version = driver_capabilities["browserVersion"]1285 major_chrome_version = chrome_version.split('.')[0]1286 chrome_dict = self.driver.__dict__["capabilities"]["chrome"]1287 chromedriver_version = chrome_dict["chromedriverVersion"]1288 chromedriver_version = chromedriver_version.split(' ')[0]1289 major_chromedriver_version = chromedriver_version.split('.')[0]1290 install_sb = (1291 "seleniumbase install chromedriver %s" % major_chrome_version)1292 if major_chromedriver_version < major_chrome_version:1293 # Upgrading the driver is required for performing hover actions1294 message = (1295 "\n"1296 "You need a newer chromedriver to perform hover actions!\n"1297 "Your version of chromedriver is: %s\n"1298 "And your version of Chrome is: %s\n"1299 "You can fix this issue by running:\n>>> %s\n"1300 "" % (chromedriver_version, chrome_version, install_sb))1301 raise Exception(message)1302 else:1303 raise Exception(e)1304 def hover_and_click(self, hover_selector, click_selector,1305 hover_by=By.CSS_SELECTOR, click_by=By.CSS_SELECTOR,1306 timeout=None):1307 """ When you want to hover over an element or dropdown menu,1308 and then click an element that appears after that. """1309 self.__check_scope()1310 if not timeout:1311 timeout = settings.SMALL_TIMEOUT1312 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1313 timeout = self.__get_new_timeout(timeout)1314 original_selector = hover_selector1315 original_by = hover_by1316 hover_selector, hover_by = self.__recalculate_selector(1317 hover_selector, hover_by)1318 hover_selector = self.convert_to_css_selector(1319 hover_selector, hover_by)1320 hover_by = By.CSS_SELECTOR1321 click_selector, click_by = self.__recalculate_selector(1322 click_selector, click_by)1323 dropdown_element = self.wait_for_element_visible(1324 hover_selector, by=hover_by, timeout=timeout)1325 self.__demo_mode_highlight_if_active(original_selector, original_by)1326 self.scroll_to(hover_selector, by=hover_by)1327 pre_action_url = self.driver.current_url1328 outdated_driver = False1329 element = None1330 try:1331 if self.mobile_emulator:1332 # On mobile, click to hover the element1333 dropdown_element.click()1334 elif self.browser == "safari":1335 # Use the workaround for hover-clicking on Safari1336 raise Exception("This Exception will be caught.")1337 else:1338 page_actions.hover_element(self.driver, dropdown_element)1339 except Exception:1340 outdated_driver = True1341 element = self.wait_for_element_present(1342 click_selector, click_by, timeout)1343 if click_by == By.LINK_TEXT:1344 self.open(self.__get_href_from_link_text(click_selector))1345 elif click_by == By.PARTIAL_LINK_TEXT:1346 self.open(self.__get_href_from_partial_link_text(1347 click_selector))1348 else:1349 self.js_click(click_selector, by=click_by)1350 if outdated_driver:1351 pass # Already did the click workaround1352 elif self.mobile_emulator:1353 self.click(click_selector, by=click_by)1354 elif not outdated_driver:1355 element = page_actions.hover_and_click(1356 self.driver, hover_selector, click_selector,1357 hover_by, click_by, timeout)1358 if self.demo_mode:1359 if self.driver.current_url != pre_action_url:1360 self.__demo_mode_pause_if_active()1361 else:1362 self.__demo_mode_pause_if_active(tiny=True)1363 elif self.slow_mode:1364 self.__slow_mode_pause_if_active()1365 return element1366 def hover_and_double_click(self, hover_selector, click_selector,1367 hover_by=By.CSS_SELECTOR,1368 click_by=By.CSS_SELECTOR,1369 timeout=None):1370 """ When you want to hover over an element or dropdown menu,1371 and then double-click an element that appears after that. """1372 self.__check_scope()1373 if not timeout:1374 timeout = settings.SMALL_TIMEOUT1375 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1376 timeout = self.__get_new_timeout(timeout)1377 original_selector = hover_selector1378 original_by = hover_by1379 hover_selector, hover_by = self.__recalculate_selector(1380 hover_selector, hover_by)1381 hover_selector = self.convert_to_css_selector(1382 hover_selector, hover_by)1383 hover_by = By.CSS_SELECTOR1384 click_selector, click_by = self.__recalculate_selector(1385 click_selector, click_by)1386 dropdown_element = self.wait_for_element_visible(1387 hover_selector, by=hover_by, timeout=timeout)1388 self.__demo_mode_highlight_if_active(original_selector, original_by)1389 self.scroll_to(hover_selector, by=hover_by)1390 pre_action_url = self.driver.current_url1391 outdated_driver = False1392 element = None1393 try:1394 page_actions.hover_element(self.driver, dropdown_element)1395 except Exception:1396 outdated_driver = True1397 element = self.wait_for_element_present(1398 click_selector, click_by, timeout)1399 if click_by == By.LINK_TEXT:1400 self.open(self.__get_href_from_link_text(click_selector))1401 elif click_by == By.PARTIAL_LINK_TEXT:1402 self.open(self.__get_href_from_partial_link_text(1403 click_selector))1404 else:1405 self.js_click(click_selector, click_by)1406 if not outdated_driver:1407 element = page_actions.hover_element_and_double_click(1408 self.driver, dropdown_element, click_selector,1409 click_by=By.CSS_SELECTOR, timeout=timeout)1410 if self.demo_mode:1411 if self.driver.current_url != pre_action_url:1412 self.__demo_mode_pause_if_active()1413 else:1414 self.__demo_mode_pause_if_active(tiny=True)1415 elif self.slow_mode:1416 self.__slow_mode_pause_if_active()1417 return element1418 def drag_and_drop(self, drag_selector, drop_selector,1419 drag_by=By.CSS_SELECTOR, drop_by=By.CSS_SELECTOR,1420 timeout=None):1421 """ Drag and drop an element from one selector to another. """1422 self.__check_scope()1423 if not timeout:1424 timeout = settings.SMALL_TIMEOUT1425 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1426 timeout = self.__get_new_timeout(timeout)1427 drag_selector, drag_by = self.__recalculate_selector(1428 drag_selector, drag_by)1429 drop_selector, drop_by = self.__recalculate_selector(1430 drop_selector, drop_by)1431 drag_element = self.wait_for_element_visible(1432 drag_selector, by=drag_by, timeout=timeout)1433 self.__demo_mode_highlight_if_active(drag_selector, drag_by)1434 self.wait_for_element_visible(1435 drop_selector, by=drop_by, timeout=timeout)1436 self.__demo_mode_highlight_if_active(drop_selector, drop_by)1437 self.scroll_to(drag_selector, by=drag_by)1438 drag_selector = self.convert_to_css_selector(1439 drag_selector, drag_by)1440 drop_selector = self.convert_to_css_selector(1441 drop_selector, drop_by)1442 drag_and_drop_script = js_utils.get_drag_and_drop_script()1443 self.safe_execute_script(1444 drag_and_drop_script + (1445 "$('%s').simulateDragDrop("1446 "{dropTarget: "1447 "'%s'});" % (drag_selector, drop_selector)))1448 if self.demo_mode:1449 self.__demo_mode_pause_if_active()1450 elif self.slow_mode:1451 self.__slow_mode_pause_if_active()1452 return drag_element1453 def drag_and_drop_with_offset(1454 self, selector, x, y, by=By.CSS_SELECTOR, timeout=None):1455 """ Drag and drop an element to an {X,Y}-offset location. """1456 self.__check_scope()1457 if not timeout:1458 timeout = settings.SMALL_TIMEOUT1459 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1460 timeout = self.__get_new_timeout(timeout)1461 selector, by = self.__recalculate_selector(selector, by)1462 css_selector = self.convert_to_css_selector(selector, by=by)1463 element = self.wait_for_element_visible(css_selector, timeout=timeout)1464 self.__demo_mode_highlight_if_active(css_selector, By.CSS_SELECTOR)1465 css_selector = re.escape(css_selector) # Add "\\" to special chars1466 css_selector = self.__escape_quotes_if_needed(css_selector)1467 script = js_utils.get_drag_and_drop_with_offset_script(1468 css_selector, x, y)1469 self.safe_execute_script(script)1470 if self.demo_mode:1471 self.__demo_mode_pause_if_active()1472 elif self.slow_mode:1473 self.__slow_mode_pause_if_active()1474 return element1475 def __select_option(self, dropdown_selector, option,1476 dropdown_by=By.CSS_SELECTOR, option_by="text",1477 timeout=None):1478 """ Selects an HTML <select> option by specification.1479 Option specifications are by "text", "index", or "value".1480 Defaults to "text" if option_by is unspecified or unknown. """1481 from selenium.webdriver.support.ui import Select1482 self.__check_scope()1483 if not timeout:1484 timeout = settings.SMALL_TIMEOUT1485 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1486 timeout = self.__get_new_timeout(timeout)1487 dropdown_selector, dropdown_by = self.__recalculate_selector(1488 dropdown_selector, dropdown_by)1489 self.wait_for_ready_state_complete()1490 element = self.wait_for_element_present(1491 dropdown_selector, by=dropdown_by, timeout=timeout)1492 if self.is_element_visible(dropdown_selector, by=dropdown_by):1493 self.__demo_mode_highlight_if_active(1494 dropdown_selector, dropdown_by)1495 pre_action_url = self.driver.current_url1496 try:1497 if option_by == "index":1498 Select(element).select_by_index(option)1499 elif option_by == "value":1500 Select(element).select_by_value(option)1501 else:1502 Select(element).select_by_visible_text(option)1503 except (StaleElementReferenceException, ENI_Exception):1504 self.wait_for_ready_state_complete()1505 time.sleep(0.14)1506 element = self.wait_for_element_present(1507 dropdown_selector, by=dropdown_by, timeout=timeout)1508 if option_by == "index":1509 Select(element).select_by_index(option)1510 elif option_by == "value":1511 Select(element).select_by_value(option)1512 else:1513 Select(element).select_by_visible_text(option)1514 if settings.WAIT_FOR_RSC_ON_CLICKS:1515 self.wait_for_ready_state_complete()1516 if self.demo_mode:1517 if self.driver.current_url != pre_action_url:1518 self.__demo_mode_pause_if_active()1519 else:1520 self.__demo_mode_pause_if_active(tiny=True)1521 elif self.slow_mode:1522 self.__slow_mode_pause_if_active()1523 def select_option_by_text(self, dropdown_selector, option,1524 dropdown_by=By.CSS_SELECTOR,1525 timeout=None):1526 """ Selects an HTML <select> option by option text.1527 @Params1528 dropdown_selector - the <select> selector1529 option - the text of the option """1530 self.__check_scope()1531 if not timeout:1532 timeout = settings.SMALL_TIMEOUT1533 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1534 timeout = self.__get_new_timeout(timeout)1535 self.__select_option(dropdown_selector, option,1536 dropdown_by=dropdown_by, option_by="text",1537 timeout=timeout)1538 def select_option_by_index(self, dropdown_selector, option,1539 dropdown_by=By.CSS_SELECTOR,1540 timeout=None):1541 """ Selects an HTML <select> option by option index.1542 @Params1543 dropdown_selector - the <select> selector1544 option - the index number of the option """1545 self.__check_scope()1546 if not timeout:1547 timeout = settings.SMALL_TIMEOUT1548 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1549 timeout = self.__get_new_timeout(timeout)1550 self.__select_option(dropdown_selector, option,1551 dropdown_by=dropdown_by, option_by="index",1552 timeout=timeout)1553 def select_option_by_value(self, dropdown_selector, option,1554 dropdown_by=By.CSS_SELECTOR,1555 timeout=None):1556 """ Selects an HTML <select> option by option value.1557 @Params1558 dropdown_selector - the <select> selector1559 option - the value property of the option """1560 self.__check_scope()1561 if not timeout:1562 timeout = settings.SMALL_TIMEOUT1563 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1564 timeout = self.__get_new_timeout(timeout)1565 self.__select_option(dropdown_selector, option,1566 dropdown_by=dropdown_by, option_by="value",1567 timeout=timeout)1568 def load_html_string(self, html_string, new_page=True):1569 """ Loads an HTML string into the web browser.1570 If new_page==True, the page will switch to: "data:text/html,"1571 If new_page==False, will load HTML into the current page. """1572 self.__check_scope()1573 soup = self.get_beautiful_soup(html_string)1574 found_base = False1575 links = soup.findAll("link")1576 href = None1577 for link in links:1578 if link.get("rel") == ["canonical"] and link.get("href"):1579 found_base = True1580 href = link.get("href")1581 href = self.get_domain_url(href)1582 if found_base and html_string.count("<head>") == 1 and (1583 html_string.count("<base") == 0):1584 html_string = html_string.replace(1585 "<head>", '<head><base href="%s">' % href)1586 elif not found_base:1587 bases = soup.findAll("base")1588 for base in bases:1589 if base.get("href"):1590 href = base.get("href")1591 if href:1592 html_string = html_string.replace(1593 'base: "."', 'base: "%s"' % href)1594 soup = self.get_beautiful_soup(html_string)1595 scripts = soup.findAll("script")1596 for script in scripts:1597 if script.get("type") != "application/json":1598 html_string = html_string.replace(str(script), "")1599 soup = self.get_beautiful_soup(html_string)1600 found_head = False1601 found_body = False1602 html_head = None1603 html_body = None1604 if soup.head and len(str(soup.head)) > 12:1605 found_head = True1606 html_head = str(soup.head)1607 html_head = re.escape(html_head)1608 html_head = self.__escape_quotes_if_needed(html_head)1609 html_head = html_head.replace('\\ ', ' ')1610 if soup.body and len(str(soup.body)) > 12:1611 found_body = True1612 html_body = str(soup.body)1613 html_body = html_body.replace("\xc2\xa0", " ")1614 html_body = html_body.replace("\xc2\xa1", "¡")1615 html_body = html_body.replace("\xc2\xa9", "©")1616 html_body = html_body.replace("\xc2\xb7", "·")1617 html_body = html_body.replace("\xc2\xbf", "¿")1618 html_body = html_body.replace("\xc3\x97", "×")1619 html_body = html_body.replace("\xc3\xb7", "÷")1620 html_body = re.escape(html_body)1621 html_body = self.__escape_quotes_if_needed(html_body)1622 html_body = html_body.replace('\\ ', ' ')1623 html_string = re.escape(html_string)1624 html_string = self.__escape_quotes_if_needed(html_string)1625 html_string = html_string.replace('\\ ', ' ')1626 if new_page:1627 self.open("data:text/html,")1628 inner_head = '''document.getElementsByTagName("head")[0].innerHTML'''1629 inner_body = '''document.getElementsByTagName("body")[0].innerHTML'''1630 if not found_body:1631 self.execute_script(1632 '''%s = \"%s\"''' % (inner_body, html_string))1633 elif found_body and not found_head:1634 self.execute_script(1635 '''%s = \"%s\"''' % (inner_body, html_body))1636 elif found_body and found_head:1637 self.execute_script(1638 '''%s = \"%s\"''' % (inner_head, html_head))1639 self.execute_script(1640 '''%s = \"%s\"''' % (inner_body, html_body))1641 else:1642 raise Exception("Logic Error!")1643 for script in scripts:1644 js_code = script.string1645 js_src = script.get("src")1646 if js_code and script.get("type") != "application/json":1647 js_code_lines = js_code.split('\n')1648 new_lines = []1649 for line in js_code_lines:1650 line = line.strip()1651 new_lines.append(line)1652 js_code = '\n'.join(new_lines)1653 js_utils.add_js_code(self.driver, js_code)1654 elif js_src:1655 js_utils.add_js_link(self.driver, js_src)1656 else:1657 pass1658 def load_html_file(self, html_file, new_page=True):1659 """ Loads a local html file into the browser from a relative file path.1660 If new_page==True, the page will switch to: "data:text/html,"1661 If new_page==False, will load HTML into the current page.1662 Local images and other local src content WILL BE IGNORED. """1663 self.__check_scope()1664 if self.__looks_like_a_page_url(html_file):1665 self.open(html_file)1666 return1667 if len(html_file) < 6 or not html_file.endswith(".html"):1668 raise Exception('Expecting a ".html" file!')1669 abs_path = os.path.abspath('.')1670 file_path = None1671 if abs_path in html_file:1672 file_path = html_file1673 else:1674 file_path = abs_path + "/%s" % html_file1675 html_string = None1676 with open(file_path, 'r') as f:1677 html_string = f.read().strip()1678 self.load_html_string(html_string, new_page)1679 def open_html_file(self, html_file):1680 """ Opens a local html file into the browser from a relative file path.1681 The URL displayed in the web browser will start with "file://". """1682 self.__check_scope()1683 if self.__looks_like_a_page_url(html_file):1684 self.open(html_file)1685 return1686 if len(html_file) < 6 or not html_file.endswith(".html"):1687 raise Exception('Expecting a ".html" file!')1688 abs_path = os.path.abspath('.')1689 file_path = None1690 if abs_path in html_file:1691 file_path = html_file1692 else:1693 file_path = abs_path + "/%s" % html_file1694 self.open("file://" + file_path)1695 def execute_script(self, script):1696 self.__check_scope()1697 return self.driver.execute_script(script)1698 def execute_async_script(self, script, timeout=None):1699 self.__check_scope()1700 if not timeout:1701 timeout = settings.EXTREME_TIMEOUT1702 return js_utils.execute_async_script(self.driver, script, timeout)1703 def safe_execute_script(self, script):1704 """ When executing a script that contains a jQuery command,1705 it's important that the jQuery library has been loaded first.1706 This method will load jQuery if it wasn't already loaded. """1707 self.__check_scope()1708 try:1709 return self.execute_script(script)1710 except Exception:1711 # The likely reason this fails is because: "jQuery is not defined"1712 self.activate_jquery() # It's a good thing we can define it here1713 return self.execute_script(script)1714 def set_window_rect(self, x, y, width, height):1715 self.__check_scope()1716 self.driver.set_window_rect(x, y, width, height)1717 self.__demo_mode_pause_if_active()1718 def set_window_size(self, width, height):1719 self.__check_scope()1720 self.driver.set_window_size(width, height)1721 self.__demo_mode_pause_if_active()1722 def maximize_window(self):1723 self.__check_scope()1724 self.driver.maximize_window()1725 self.__demo_mode_pause_if_active()1726 def switch_to_frame(self, frame, timeout=None):1727 """1728 Wait for an iframe to appear, and switch to it. This should be1729 usable as a drop-in replacement for driver.switch_to.frame().1730 The iframe identifier can be a selector, an index, an id, a name,1731 or a web element, but scrolling to the iframe first will only occur1732 for visible iframes with a string selector.1733 @Params1734 frame - the frame element, name, id, index, or selector1735 timeout - the time to wait for the alert in seconds1736 """1737 self.__check_scope()1738 if not timeout:1739 timeout = settings.SMALL_TIMEOUT1740 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1741 timeout = self.__get_new_timeout(timeout)1742 if type(frame) is str and self.is_element_visible(frame):1743 try:1744 self.scroll_to(frame, timeout=1)1745 except Exception:1746 pass1747 page_actions.switch_to_frame(self.driver, frame, timeout)1748 def switch_to_default_content(self):1749 """ Brings driver control outside the current iframe.1750 (If driver control is inside an iframe, the driver control1751 will be set to one level above the current frame. If the driver1752 control is not currently in an iframe, nothing will happen.) """1753 self.__check_scope()1754 self.driver.switch_to.default_content()1755 def open_new_window(self, switch_to=True):1756 """ Opens a new browser tab/window and switches to it by default. """1757 self.__check_scope()1758 self.driver.execute_script("window.open('');")1759 time.sleep(0.01)1760 if switch_to:1761 self.switch_to_window(len(self.driver.window_handles) - 1)1762 def switch_to_window(self, window, timeout=None):1763 self.__check_scope()1764 if not timeout:1765 timeout = settings.SMALL_TIMEOUT1766 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1767 timeout = self.__get_new_timeout(timeout)1768 page_actions.switch_to_window(self.driver, window, timeout)1769 def switch_to_default_window(self):1770 self.switch_to_window(0)1771 def get_new_driver(self, browser=None, headless=None, locale_code=None,1772 servername=None, port=None, proxy=None, agent=None,1773 switch_to=True, cap_file=None, cap_string=None,1774 disable_csp=None, enable_ws=None, enable_sync=None,1775 use_auto_ext=None, no_sandbox=None, disable_gpu=None,1776 incognito=None, guest_mode=None,1777 devtools=None, remote_debug=None,1778 swiftshader=None, block_images=None, user_data_dir=None,1779 extension_zip=None, extension_dir=None, is_mobile=False,1780 d_width=None, d_height=None, d_p_r=None):1781 """ This method spins up an extra browser for tests that require1782 more than one. The first browser is already provided by tests1783 that import base_case.BaseCase from seleniumbase. If parameters1784 aren't specified, the method uses the same as the default driver.1785 @Params1786 browser - the browser to use. (Ex: "chrome", "firefox")1787 headless - the option to run webdriver in headless mode1788 locale_code - the Language Locale Code for the web browser1789 servername - if using a Selenium Grid, set the host address here1790 port - if using a Selenium Grid, set the host port here1791 proxy - if using a proxy server, specify the "host:port" combo here1792 switch_to - the option to switch to the new driver (default = True)1793 cap_file - the file containing desired capabilities for the browser1794 cap_string - the string with desired capabilities for the browser1795 disable_csp - an option to disable Chrome's Content Security Policy1796 enable_ws - the option to enable the Web Security feature (Chrome)1797 enable_sync - the option to enable the Chrome Sync feature (Chrome)1798 use_auto_ext - the option to enable Chrome's Automation Extension1799 no_sandbox - the option to enable the "No-Sandbox" feature (Chrome)1800 disable_gpu - the option to enable Chrome's "Disable GPU" feature1801 incognito - the option to enable Chrome's Incognito mode (Chrome)1802 guest - the option to enable Chrome's Guest mode (Chrome)1803 devtools - the option to open Chrome's DevTools on start (Chrome)1804 remote_debug - the option to enable Chrome's Remote Debugger1805 swiftshader - the option to use Chrome's swiftshader (Chrome-only)1806 block_images - the option to block images from loading (Chrome)1807 user_data_dir - Chrome's User Data Directory to use (Chrome-only)1808 extension_zip - A Chrome Extension ZIP file to use (Chrome-only)1809 extension_dir - A Chrome Extension folder to use (Chrome-only)1810 is_mobile - the option to use the mobile emulator (Chrome-only)1811 d_width - the device width of the mobile emulator (Chrome-only)1812 d_height - the device height of the mobile emulator (Chrome-only)1813 d_p_r - the device pixel ratio of the mobile emulator (Chrome-only)1814 """1815 self.__check_scope()1816 if self.browser == "remote" and self.servername == "localhost":1817 raise Exception('Cannot use "remote" browser driver on localhost!'1818 ' Did you mean to connect to a remote Grid server'1819 ' such as BrowserStack or Sauce Labs? In that'1820 ' case, you must specify the "server" and "port"'1821 ' parameters on the command line! '1822 'Example: '1823 '--server=user:key@hub.browserstack.com --port=80')1824 browserstack_ref = (1825 'https://browserstack.com/automate/capabilities')1826 sauce_labs_ref = (1827 'https://wiki.saucelabs.com/display/DOCS/Platform+Configurator#/')1828 if self.browser == "remote" and not (self.cap_file or self.cap_string):1829 raise Exception('Need to specify a desired capabilities file when '1830 'using "--browser=remote". Add "--cap_file=FILE". '1831 'File should be in the Python format used by: '1832 '%s OR '1833 '%s '1834 'See SeleniumBase/examples/sample_cap_file_BS.py '1835 'and SeleniumBase/examples/sample_cap_file_SL.py'1836 % (browserstack_ref, sauce_labs_ref))1837 if browser is None:1838 browser = self.browser1839 browser_name = browser1840 if headless is None:1841 headless = self.headless1842 if locale_code is None:1843 locale_code = self.locale_code1844 if servername is None:1845 servername = self.servername1846 if port is None:1847 port = self.port1848 use_grid = False1849 if servername != "localhost":1850 # Use Selenium Grid (Use "127.0.0.1" for localhost Grid)1851 use_grid = True1852 proxy_string = proxy1853 if proxy_string is None:1854 proxy_string = self.proxy_string1855 user_agent = agent1856 if user_agent is None:1857 user_agent = self.user_agent1858 if disable_csp is None:1859 disable_csp = self.disable_csp1860 if enable_ws is None:1861 enable_ws = self.enable_ws1862 if enable_sync is None:1863 enable_sync = self.enable_sync1864 if use_auto_ext is None:1865 use_auto_ext = self.use_auto_ext1866 if no_sandbox is None:1867 no_sandbox = self.no_sandbox1868 if disable_gpu is None:1869 disable_gpu = self.disable_gpu1870 if incognito is None:1871 incognito = self.incognito1872 if guest_mode is None:1873 guest_mode = self.guest_mode1874 if devtools is None:1875 devtools = self.devtools1876 if remote_debug is None:1877 remote_debug = self.remote_debug1878 if swiftshader is None:1879 swiftshader = self.swiftshader1880 if block_images is None:1881 block_images = self.block_images1882 if user_data_dir is None:1883 user_data_dir = self.user_data_dir1884 if extension_zip is None:1885 extension_zip = self.extension_zip1886 if extension_dir is None:1887 extension_dir = self.extension_dir1888 test_id = self.__get_test_id()1889 if cap_file is None:1890 cap_file = self.cap_file1891 if cap_string is None:1892 cap_string = self.cap_string1893 if is_mobile is None:1894 is_mobile = False1895 if d_width is None:1896 d_width = self.__device_width1897 if d_height is None:1898 d_height = self.__device_height1899 if d_p_r is None:1900 d_p_r = self.__device_pixel_ratio1901 valid_browsers = constants.ValidBrowsers.valid_browsers1902 if browser_name not in valid_browsers:1903 raise Exception("Browser: {%s} is not a valid browser option. "1904 "Valid options = {%s}" % (browser, valid_browsers))1905 # Launch a web browser1906 from seleniumbase.core import browser_launcher1907 new_driver = browser_launcher.get_driver(browser_name=browser_name,1908 headless=headless,1909 locale_code=locale_code,1910 use_grid=use_grid,1911 servername=servername,1912 port=port,1913 proxy_string=proxy_string,1914 user_agent=user_agent,1915 cap_file=cap_file,1916 cap_string=cap_string,1917 disable_csp=disable_csp,1918 enable_ws=enable_ws,1919 enable_sync=enable_sync,1920 use_auto_ext=use_auto_ext,1921 no_sandbox=no_sandbox,1922 disable_gpu=disable_gpu,1923 incognito=incognito,1924 guest_mode=guest_mode,1925 devtools=devtools,1926 remote_debug=remote_debug,1927 swiftshader=swiftshader,1928 block_images=block_images,1929 user_data_dir=user_data_dir,1930 extension_zip=extension_zip,1931 extension_dir=extension_dir,1932 test_id=test_id,1933 mobile_emulator=is_mobile,1934 device_width=d_width,1935 device_height=d_height,1936 device_pixel_ratio=d_p_r)1937 self._drivers_list.append(new_driver)1938 if switch_to:1939 self.driver = new_driver1940 if self.headless:1941 # Make sure the invisible browser window is big enough1942 width = settings.HEADLESS_START_WIDTH1943 height = settings.HEADLESS_START_HEIGHT1944 try:1945 self.driver.set_window_size(width, height)1946 self.wait_for_ready_state_complete()1947 except Exception:1948 # This shouldn't fail, but in case it does,1949 # get safely through setUp() so that1950 # WebDrivers can get closed during tearDown().1951 pass1952 else:1953 if self.browser == "chrome" or self.browser == "edge":1954 width = settings.CHROME_START_WIDTH1955 height = settings.CHROME_START_HEIGHT1956 try:1957 if self.maximize_option:1958 self.driver.maximize_window()1959 else:1960 self.driver.set_window_size(width, height)1961 self.wait_for_ready_state_complete()1962 except Exception:1963 pass # Keep existing browser resolution1964 elif self.browser == "firefox":1965 width = settings.CHROME_START_WIDTH1966 try:1967 if self.maximize_option:1968 self.driver.maximize_window()1969 else:1970 self.driver.set_window_size(width, 720)1971 self.wait_for_ready_state_complete()1972 except Exception:1973 pass # Keep existing browser resolution1974 elif self.browser == "safari":1975 width = settings.CHROME_START_WIDTH1976 if self.maximize_option:1977 try:1978 self.driver.maximize_window()1979 self.wait_for_ready_state_complete()1980 except Exception:1981 pass # Keep existing browser resolution1982 else:1983 try:1984 self.driver.set_window_rect(10, 30, width, 630)1985 except Exception:1986 pass1987 elif self.browser == "opera":1988 width = settings.CHROME_START_WIDTH1989 if self.maximize_option:1990 try:1991 self.driver.maximize_window()1992 self.wait_for_ready_state_complete()1993 except Exception:1994 pass # Keep existing browser resolution1995 else:1996 try:1997 self.driver.set_window_rect(10, 30, width, 700)1998 except Exception:1999 pass2000 if self.start_page and len(self.start_page) >= 4:2001 if page_utils.is_valid_url(self.start_page):2002 self.open(self.start_page)2003 else:2004 new_start_page = "http://" + self.start_page2005 if page_utils.is_valid_url(new_start_page):2006 self.open(new_start_page)2007 return new_driver2008 def switch_to_driver(self, driver):2009 """ Sets self.driver to the specified driver.2010 You may need this if using self.get_new_driver() in your code. """2011 self.driver = driver2012 def switch_to_default_driver(self):2013 """ Sets self.driver to the default/original driver. """2014 self.__check_scope()2015 self.driver = self._default_driver2016 def save_screenshot(self, name, folder=None):2017 """ The screenshot will be in PNG format. """2018 self.wait_for_ready_state_complete()2019 return page_actions.save_screenshot(self.driver, name, folder)2020 def save_page_source(self, name, folder=None):2021 """ Saves the page HTML to the current directory (or given subfolder).2022 If the folder specified doesn't exist, it will get created.2023 @Params2024 name - The file name to save the current page's HTML to.2025 folder - The folder to save the file to. (Default = current folder)2026 """2027 self.wait_for_ready_state_complete()2028 return page_actions.save_page_source(self.driver, name, folder)2029 def save_cookies(self, name="cookies.txt"):2030 """ Saves the page cookies to the "saved_cookies" folder. """2031 self.wait_for_ready_state_complete()2032 cookies = self.driver.get_cookies()2033 json_cookies = json.dumps(cookies)2034 if name.endswith('/'):2035 raise Exception("Invalid filename for Cookies!")2036 if '/' in name:2037 name = name.split('/')[-1]2038 if len(name) < 1:2039 raise Exception("Filename for Cookies is too short!")2040 if not name.endswith(".txt"):2041 name = name + ".txt"2042 folder = constants.SavedCookies.STORAGE_FOLDER2043 abs_path = os.path.abspath('.')2044 file_path = abs_path + "/%s" % folder2045 if not os.path.exists(file_path):2046 os.makedirs(file_path)2047 cookies_file_path = "%s/%s" % (file_path, name)2048 cookies_file = codecs.open(cookies_file_path, "w+", encoding="utf-8")2049 cookies_file.writelines(json_cookies)2050 cookies_file.close()2051 def load_cookies(self, name="cookies.txt"):2052 """ Loads the page cookies from the "saved_cookies" folder. """2053 self.wait_for_ready_state_complete()2054 if name.endswith('/'):2055 raise Exception("Invalid filename for Cookies!")2056 if '/' in name:2057 name = name.split('/')[-1]2058 if len(name) < 1:2059 raise Exception("Filename for Cookies is too short!")2060 if not name.endswith(".txt"):2061 name = name + ".txt"2062 folder = constants.SavedCookies.STORAGE_FOLDER2063 abs_path = os.path.abspath('.')2064 file_path = abs_path + "/%s" % folder2065 cookies_file_path = "%s/%s" % (file_path, name)2066 json_cookies = None2067 with open(cookies_file_path, 'r') as f:2068 json_cookies = f.read().strip()2069 cookies = json.loads(json_cookies)2070 for cookie in cookies:2071 if 'expiry' in cookie:2072 del cookie['expiry']2073 self.driver.add_cookie(cookie)2074 def delete_all_cookies(self):2075 """ Deletes all cookies in the web browser.2076 Does NOT delete the saved cookies file. """2077 self.wait_for_ready_state_complete()2078 self.driver.delete_all_cookies()2079 def delete_saved_cookies(self, name="cookies.txt"):2080 """ Deletes the cookies file from the "saved_cookies" folder.2081 Does NOT delete the cookies from the web browser. """2082 self.wait_for_ready_state_complete()2083 if name.endswith('/'):2084 raise Exception("Invalid filename for Cookies!")2085 if '/' in name:2086 name = name.split('/')[-1]2087 if len(name) < 1:2088 raise Exception("Filename for Cookies is too short!")2089 if not name.endswith(".txt"):2090 name = name + ".txt"2091 folder = constants.SavedCookies.STORAGE_FOLDER2092 abs_path = os.path.abspath('.')2093 file_path = abs_path + "/%s" % folder2094 cookies_file_path = "%s/%s" % (file_path, name)2095 if os.path.exists(cookies_file_path):2096 if cookies_file_path.endswith('.txt'):2097 os.remove(cookies_file_path)2098 def wait_for_ready_state_complete(self, timeout=None):2099 self.__check_scope()2100 if not timeout:2101 timeout = settings.EXTREME_TIMEOUT2102 if self.timeout_multiplier and timeout == settings.EXTREME_TIMEOUT:2103 timeout = self.__get_new_timeout(timeout)2104 is_ready = js_utils.wait_for_ready_state_complete(self.driver, timeout)2105 self.wait_for_angularjs(timeout=settings.MINI_TIMEOUT)2106 if self.js_checking_on:2107 self.assert_no_js_errors()2108 if self.ad_block_on:2109 # If the ad_block feature is enabled, then block ads for new URLs2110 current_url = self.get_current_url()2111 if not current_url == self.__last_page_load_url:2112 time.sleep(0.02)2113 self.ad_block()2114 time.sleep(0.02)2115 if self.is_element_present("iframe"):2116 time.sleep(0.1) # iframe ads take slightly longer to load2117 self.ad_block() # Do ad_block on slower-loading iframes2118 self.__last_page_load_url = current_url2119 return is_ready2120 def wait_for_angularjs(self, timeout=None, **kwargs):2121 self.__check_scope()2122 if not timeout:2123 timeout = settings.LARGE_TIMEOUT2124 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2125 timeout = self.__get_new_timeout(timeout)2126 js_utils.wait_for_angularjs(self.driver, timeout, **kwargs)2127 def sleep(self, seconds):2128 self.__check_scope()2129 if not sb_config.time_limit:2130 time.sleep(seconds)2131 elif seconds <= 0.3:2132 shared_utils.check_if_time_limit_exceeded()2133 time.sleep(seconds)2134 shared_utils.check_if_time_limit_exceeded()2135 else:2136 start_ms = time.time() * 1000.02137 stop_ms = start_ms + (seconds * 1000.0)2138 for x in range(int(seconds * 5)):2139 shared_utils.check_if_time_limit_exceeded()2140 now_ms = time.time() * 1000.02141 if now_ms >= stop_ms:2142 break2143 time.sleep(0.2)2144 def install_addon(self, xpi_file):2145 """ Installs a Firefox add-on instantly at run-time.2146 @Params2147 xpi_file - A file archive in .xpi format. """2148 self.wait_for_ready_state_complete()2149 if self.browser != "firefox":2150 raise Exception(2151 "install_addon(xpi_file) is for Firefox ONLY!\n"2152 "To load a Chrome extension, use the comamnd-line:\n"2153 "--extension_zip=CRX_FILE OR --extension_dir=DIR")2154 xpi_path = os.path.abspath(xpi_file)2155 self.driver.install_addon(xpi_path, temporary=True)2156 def activate_design_mode(self):2157 # Activate Chrome's Design Mode, which lets you edit a site directly.2158 # See: https://twitter.com/sulco/status/11775591505633443842159 self.wait_for_ready_state_complete()2160 script = ("""document.designMode = 'on';""")2161 self.execute_script(script)2162 def deactivate_design_mode(self):2163 # Deactivate Chrome's Design Mode.2164 self.wait_for_ready_state_complete()2165 script = ("""document.designMode = 'off';""")2166 self.execute_script(script)2167 def activate_jquery(self):2168 """ If "jQuery is not defined", use this method to activate it for use.2169 This happens because jQuery is not always defined on web sites. """2170 self.wait_for_ready_state_complete()2171 js_utils.activate_jquery(self.driver)2172 self.wait_for_ready_state_complete()2173 def __are_quotes_escaped(self, string):2174 return js_utils.are_quotes_escaped(string)2175 def __escape_quotes_if_needed(self, string):2176 return js_utils.escape_quotes_if_needed(string)2177 def bring_to_front(self, selector, by=By.CSS_SELECTOR):2178 """ Updates the Z-index of a page element to bring it into view.2179 Useful when getting a WebDriverException, such as the one below:2180 { Element is not clickable at point (#, #).2181 Other element would receive the click: ... } """2182 self.__check_scope()2183 selector, by = self.__recalculate_selector(selector, by)2184 self.wait_for_element_visible(2185 selector, by=by, timeout=settings.SMALL_TIMEOUT)2186 try:2187 selector = self.convert_to_css_selector(selector, by=by)2188 except Exception:2189 # Don't run action if can't convert to CSS_Selector for JavaScript2190 return2191 selector = re.escape(selector)2192 selector = self.__escape_quotes_if_needed(selector)2193 script = ("""document.querySelector('%s').style.zIndex = '999999';"""2194 % selector)2195 self.execute_script(script)2196 def highlight_click(self, selector, by=By.CSS_SELECTOR,2197 loops=3, scroll=True):2198 self.__check_scope()2199 if not self.demo_mode:2200 self.highlight(selector, by=by, loops=loops, scroll=scroll)2201 self.click(selector, by=by)2202 def highlight_update_text(self, selector, text, by=By.CSS_SELECTOR,2203 loops=3, scroll=True):2204 self.__check_scope()2205 if not self.demo_mode:2206 self.highlight(selector, by=by, loops=loops, scroll=scroll)2207 self.update_text(selector, text, by=by)2208 def highlight(self, selector, by=By.CSS_SELECTOR,2209 loops=None, scroll=True):2210 """ This method uses fancy JavaScript to highlight an element.2211 Used during demo_mode.2212 @Params2213 selector - the selector of the element to find2214 by - the type of selector to search by (Default: CSS)2215 loops - # of times to repeat the highlight animation2216 (Default: 4. Each loop lasts for about 0.18s)2217 scroll - the option to scroll to the element first (Default: True)2218 """2219 self.__check_scope()2220 selector, by = self.__recalculate_selector(selector, by, xp_ok=False)2221 element = self.wait_for_element_visible(2222 selector, by=by, timeout=settings.SMALL_TIMEOUT)2223 if not loops:2224 loops = settings.HIGHLIGHTS2225 if scroll:2226 try:2227 if self.browser != "safari":2228 scroll_distance = js_utils.get_scroll_distance_to_element(2229 self.driver, element)2230 if abs(scroll_distance) > SSMD:2231 self.__jquery_slow_scroll_to(selector, by)2232 else:2233 self.__slow_scroll_to_element(element)2234 else:2235 self.__jquery_slow_scroll_to(selector, by)2236 except Exception:2237 self.wait_for_ready_state_complete()2238 time.sleep(0.12)2239 element = self.wait_for_element_visible(2240 selector, by=by, timeout=settings.SMALL_TIMEOUT)2241 self.__slow_scroll_to_element(element)2242 try:2243 selector = self.convert_to_css_selector(selector, by=by)2244 except Exception:2245 # Don't highlight if can't convert to CSS_SELECTOR2246 return2247 if self.highlights:2248 loops = self.highlights2249 if self.browser == "ie":2250 loops = 1 # Override previous setting because IE is slow2251 loops = int(loops)2252 o_bs = '' # original_box_shadow2253 try:2254 style = element.get_attribute('style')2255 except Exception:2256 self.wait_for_ready_state_complete()2257 time.sleep(0.12)2258 element = self.wait_for_element_visible(2259 selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT)2260 style = element.get_attribute('style')2261 if style:2262 if 'box-shadow: ' in style:2263 box_start = style.find('box-shadow: ')2264 box_end = style.find(';', box_start) + 12265 original_box_shadow = style[box_start:box_end]2266 o_bs = original_box_shadow2267 if ":contains" not in selector and ":first" not in selector:2268 selector = re.escape(selector)2269 selector = self.__escape_quotes_if_needed(selector)2270 self.__highlight_with_js(selector, loops, o_bs)2271 else:2272 selector = self.__make_css_match_first_element_only(selector)2273 selector = re.escape(selector)2274 selector = self.__escape_quotes_if_needed(selector)2275 try:2276 self.__highlight_with_jquery(selector, loops, o_bs)2277 except Exception:2278 pass # JQuery probably couldn't load. Skip highlighting.2279 time.sleep(0.065)2280 def __highlight_with_js(self, selector, loops, o_bs):2281 self.wait_for_ready_state_complete()2282 js_utils.highlight_with_js(self.driver, selector, loops, o_bs)2283 def __highlight_with_jquery(self, selector, loops, o_bs):2284 self.wait_for_ready_state_complete()2285 js_utils.highlight_with_jquery(self.driver, selector, loops, o_bs)2286 def press_up_arrow(self, selector="html", times=1, by=By.CSS_SELECTOR):2287 """ Simulates pressing the UP Arrow on the keyboard.2288 By default, "html" will be used as the CSS Selector target.2289 You can specify how many times in-a-row the action happens. """2290 self.__check_scope()2291 if times < 1:2292 return2293 element = self.wait_for_element_present(selector)2294 self.__demo_mode_highlight_if_active(selector, by)2295 if not self.demo_mode and not self.slow_mode:2296 self.__scroll_to_element(element, selector, by)2297 for i in range(int(times)):2298 try:2299 element.send_keys(Keys.ARROW_UP)2300 except Exception:2301 self.wait_for_ready_state_complete()2302 element = self.wait_for_element_visible(selector)2303 element.send_keys(Keys.ARROW_UP)2304 time.sleep(0.01)2305 if self.slow_mode:2306 time.sleep(0.1)2307 def press_down_arrow(self, selector="html", times=1, by=By.CSS_SELECTOR):2308 """ Simulates pressing the DOWN Arrow on the keyboard.2309 By default, "html" will be used as the CSS Selector target.2310 You can specify how many times in-a-row the action happens. """2311 self.__check_scope()2312 if times < 1:2313 return2314 element = self.wait_for_element_present(selector)2315 self.__demo_mode_highlight_if_active(selector, by)2316 if not self.demo_mode and not self.slow_mode:2317 self.__scroll_to_element(element, selector, by)2318 for i in range(int(times)):2319 try:2320 element.send_keys(Keys.ARROW_DOWN)2321 except Exception:2322 self.wait_for_ready_state_complete()2323 element = self.wait_for_element_visible(selector)2324 element.send_keys(Keys.ARROW_DOWN)2325 time.sleep(0.01)2326 if self.slow_mode:2327 time.sleep(0.1)2328 def press_left_arrow(self, selector="html", times=1, by=By.CSS_SELECTOR):2329 """ Simulates pressing the LEFT Arrow on the keyboard.2330 By default, "html" will be used as the CSS Selector target.2331 You can specify how many times in-a-row the action happens. """2332 self.__check_scope()2333 if times < 1:2334 return2335 element = self.wait_for_element_present(selector)2336 self.__demo_mode_highlight_if_active(selector, by)2337 if not self.demo_mode and not self.slow_mode:2338 self.__scroll_to_element(element, selector, by)2339 for i in range(int(times)):2340 try:2341 element.send_keys(Keys.ARROW_LEFT)2342 except Exception:2343 self.wait_for_ready_state_complete()2344 element = self.wait_for_element_visible(selector)2345 element.send_keys(Keys.ARROW_LEFT)2346 time.sleep(0.01)2347 if self.slow_mode:2348 time.sleep(0.1)2349 def press_right_arrow(self, selector="html", times=1, by=By.CSS_SELECTOR):2350 """ Simulates pressing the RIGHT Arrow on the keyboard.2351 By default, "html" will be used as the CSS Selector target.2352 You can specify how many times in-a-row the action happens. """2353 self.__check_scope()2354 if times < 1:2355 return2356 element = self.wait_for_element_present(selector)2357 self.__demo_mode_highlight_if_active(selector, by)2358 if not self.demo_mode and not self.slow_mode:2359 self.__scroll_to_element(element, selector, by)2360 for i in range(int(times)):2361 try:2362 element.send_keys(Keys.ARROW_RIGHT)2363 except Exception:2364 self.wait_for_ready_state_complete()2365 element = self.wait_for_element_visible(selector)2366 element.send_keys(Keys.ARROW_RIGHT)2367 time.sleep(0.01)2368 if self.slow_mode:2369 time.sleep(0.1)2370 def scroll_to(self, selector, by=By.CSS_SELECTOR, timeout=None):2371 ''' Fast scroll to destination '''2372 self.__check_scope()2373 if not timeout:2374 timeout = settings.SMALL_TIMEOUT2375 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2376 timeout = self.__get_new_timeout(timeout)2377 if self.demo_mode or self.slow_mode:2378 self.slow_scroll_to(selector, by=by, timeout=timeout)2379 return2380 element = self.wait_for_element_visible(2381 selector, by=by, timeout=timeout)2382 try:2383 self.__scroll_to_element(element, selector, by)2384 except (StaleElementReferenceException, ENI_Exception):2385 self.wait_for_ready_state_complete()2386 time.sleep(0.12)2387 element = self.wait_for_element_visible(2388 selector, by=by, timeout=timeout)2389 self.__scroll_to_element(element, selector, by)2390 def slow_scroll_to(self, selector, by=By.CSS_SELECTOR, timeout=None):2391 ''' Slow motion scroll to destination '''2392 self.__check_scope()2393 if not timeout:2394 timeout = settings.SMALL_TIMEOUT2395 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2396 timeout = self.__get_new_timeout(timeout)2397 selector, by = self.__recalculate_selector(selector, by)2398 element = self.wait_for_element_visible(2399 selector, by=by, timeout=timeout)2400 try:2401 scroll_distance = js_utils.get_scroll_distance_to_element(2402 self.driver, element)2403 if abs(scroll_distance) > SSMD:2404 self.__jquery_slow_scroll_to(selector, by)2405 else:2406 self.__slow_scroll_to_element(element)2407 except Exception:2408 self.wait_for_ready_state_complete()2409 time.sleep(0.12)2410 element = self.wait_for_element_visible(2411 selector, by=by, timeout=timeout)2412 self.__slow_scroll_to_element(element)2413 def scroll_to_top(self):2414 """ Scroll to the top of the page. """2415 self.__check_scope()2416 scroll_script = "window.scrollTo(0, 0);"2417 try:2418 self.execute_script(scroll_script)2419 time.sleep(0.012)2420 return True2421 except Exception:2422 return False2423 def scroll_to_bottom(self):2424 """ Scroll to the bottom of the page. """2425 self.__check_scope()2426 scroll_script = "window.scrollTo(0, 10000);"2427 try:2428 self.execute_script(scroll_script)2429 time.sleep(0.012)2430 return True2431 except Exception:2432 return False2433 def click_xpath(self, xpath):2434 # Technically self.click() will automatically detect an xpath selector,2435 # so self.click_xpath() is just a longer name for the same action.2436 self.click(xpath, by=By.XPATH)2437 def js_click(self, selector, by=By.CSS_SELECTOR, all_matches=False):2438 """ Clicks an element using JavaScript.2439 Can be used to click hidden / invisible elements.2440 If "all_matches" is False, only the first match is clicked. """2441 self.wait_for_ready_state_complete()2442 selector, by = self.__recalculate_selector(selector, by, xp_ok=False)2443 if by == By.LINK_TEXT:2444 message = (2445 "Pure JavaScript doesn't support clicking by Link Text. "2446 "You may want to use self.jquery_click() instead, which "2447 "allows this with :contains(), assuming jQuery isn't blocked. "2448 "For now, self.js_click() will use a regular WebDriver click.")2449 logging.debug(message)2450 self.click(selector, by=by)2451 return2452 element = self.wait_for_element_present(2453 selector, by=by, timeout=settings.SMALL_TIMEOUT)2454 if self.is_element_visible(selector, by=by):2455 self.__demo_mode_highlight_if_active(selector, by)2456 if not self.demo_mode and not self.slow_mode:2457 success = js_utils.scroll_to_element(self.driver, element)2458 if not success:2459 self.wait_for_ready_state_complete()2460 timeout = settings.SMALL_TIMEOUT2461 element = page_actions.wait_for_element_present(2462 self.driver, selector, by, timeout=timeout)2463 css_selector = self.convert_to_css_selector(selector, by=by)2464 css_selector = re.escape(css_selector) # Add "\\" to special chars2465 css_selector = self.__escape_quotes_if_needed(css_selector)2466 if not all_matches:2467 if ":contains\\(" not in css_selector:2468 self.__js_click(selector, by=by)2469 else:2470 click_script = """jQuery('%s')[0].click();""" % css_selector2471 self.safe_execute_script(click_script)2472 else:2473 if ":contains\\(" not in css_selector:2474 self.__js_click_all(selector, by=by)2475 else:2476 click_script = """jQuery('%s').click();""" % css_selector2477 self.safe_execute_script(click_script)2478 self.wait_for_ready_state_complete()2479 self.__demo_mode_pause_if_active()2480 def js_click_all(self, selector, by=By.CSS_SELECTOR):2481 """ Clicks all matching elements using pure JS. (No jQuery) """2482 self.js_click(selector, by=By.CSS_SELECTOR, all_matches=True)2483 def jquery_click(self, selector, by=By.CSS_SELECTOR):2484 """ Clicks an element using jQuery. (Different from using pure JS.)2485 Can be used to click hidden / invisible elements. """2486 self.__check_scope()2487 selector, by = self.__recalculate_selector(selector, by, xp_ok=False)2488 self.wait_for_element_present(2489 selector, by=by, timeout=settings.SMALL_TIMEOUT)2490 if self.is_element_visible(selector, by=by):2491 self.__demo_mode_highlight_if_active(selector, by)2492 selector = self.convert_to_css_selector(selector, by=by)2493 selector = self.__make_css_match_first_element_only(selector)2494 click_script = """jQuery('%s')[0].click();""" % selector2495 self.safe_execute_script(click_script)2496 self.__demo_mode_pause_if_active()2497 def jquery_click_all(self, selector, by=By.CSS_SELECTOR):2498 """ Clicks all matching elements using jQuery. """2499 self.__check_scope()2500 selector, by = self.__recalculate_selector(selector, by, xp_ok=False)2501 self.wait_for_element_present(2502 selector, by=by, timeout=settings.SMALL_TIMEOUT)2503 if self.is_element_visible(selector, by=by):2504 self.__demo_mode_highlight_if_active(selector, by)2505 css_selector = self.convert_to_css_selector(selector, by=by)2506 click_script = """jQuery('%s').click();""" % css_selector2507 self.safe_execute_script(click_script)2508 self.__demo_mode_pause_if_active()2509 def hide_element(self, selector, by=By.CSS_SELECTOR):2510 """ Hide the first element on the page that matches the selector. """2511 self.__check_scope()2512 selector, by = self.__recalculate_selector(selector, by)2513 selector = self.convert_to_css_selector(selector, by=by)2514 selector = self.__make_css_match_first_element_only(selector)2515 hide_script = """jQuery('%s').hide();""" % selector2516 self.safe_execute_script(hide_script)2517 def hide_elements(self, selector, by=By.CSS_SELECTOR):2518 """ Hide all elements on the page that match the selector. """2519 self.__check_scope()2520 selector, by = self.__recalculate_selector(selector, by)2521 selector = self.convert_to_css_selector(selector, by=by)2522 hide_script = """jQuery('%s').hide();""" % selector2523 self.safe_execute_script(hide_script)2524 def show_element(self, selector, by=By.CSS_SELECTOR):2525 """ Show the first element on the page that matches the selector. """2526 self.__check_scope()2527 selector, by = self.__recalculate_selector(selector, by)2528 selector = self.convert_to_css_selector(selector, by=by)2529 selector = self.__make_css_match_first_element_only(selector)2530 show_script = """jQuery('%s').show(0);""" % selector2531 self.safe_execute_script(show_script)2532 def show_elements(self, selector, by=By.CSS_SELECTOR):2533 """ Show all elements on the page that match the selector. """2534 self.__check_scope()2535 selector, by = self.__recalculate_selector(selector, by)2536 selector = self.convert_to_css_selector(selector, by=by)2537 show_script = """jQuery('%s').show(0);""" % selector2538 self.safe_execute_script(show_script)2539 def remove_element(self, selector, by=By.CSS_SELECTOR):2540 """ Remove the first element on the page that matches the selector. """2541 self.__check_scope()2542 selector, by = self.__recalculate_selector(selector, by)2543 selector = self.convert_to_css_selector(selector, by=by)2544 selector = self.__make_css_match_first_element_only(selector)2545 remove_script = """jQuery('%s').remove();""" % selector2546 self.safe_execute_script(remove_script)2547 def remove_elements(self, selector, by=By.CSS_SELECTOR):2548 """ Remove all elements on the page that match the selector. """2549 self.__check_scope()2550 selector, by = self.__recalculate_selector(selector, by)2551 selector = self.convert_to_css_selector(selector, by=by)2552 remove_script = """jQuery('%s').remove();""" % selector2553 self.safe_execute_script(remove_script)2554 def ad_block(self):2555 """ Block ads that appear on the current web page. """2556 from seleniumbase.config import ad_block_list2557 self.__check_scope()2558 for css_selector in ad_block_list.AD_BLOCK_LIST:2559 css_selector = re.escape(css_selector) # Add "\\" to special chars2560 css_selector = self.__escape_quotes_if_needed(css_selector)2561 script = ("""var $elements = document.querySelectorAll('%s');2562 var index = 0, length = $elements.length;2563 for(; index < length; index++){2564 $elements[index].remove();}"""2565 % css_selector)2566 try:2567 self.execute_script(script)2568 except Exception:2569 pass # Don't fail test if ad_blocking fails2570 def get_domain_url(self, url):2571 self.__check_scope()2572 return page_utils.get_domain_url(url)2573 def get_beautiful_soup(self, source=None):2574 """ BeautifulSoup is a toolkit for dissecting an HTML document2575 and extracting what you need. It's great for screen-scraping!2576 See: https://www.crummy.com/software/BeautifulSoup/bs4/doc/ """2577 from bs4 import BeautifulSoup2578 if not source:2579 source = self.get_page_source()2580 soup = BeautifulSoup(source, "html.parser")2581 return soup2582 def get_unique_links(self):2583 """ Get all unique links in the html of the page source.2584 Page links include those obtained from:2585 "a"->"href", "img"->"src", "link"->"href", and "script"->"src". """2586 page_url = self.get_current_url()2587 soup = self.get_beautiful_soup(self.get_page_source())2588 links = page_utils._get_unique_links(page_url, soup)2589 return links2590 def get_link_status_code(self, link, allow_redirects=False, timeout=5):2591 """ Get the status code of a link.2592 If the timeout is exceeded, will return a 404.2593 For a list of available status codes, see:2594 https://en.wikipedia.org/wiki/List_of_HTTP_status_codes """2595 status_code = page_utils._get_link_status_code(2596 link, allow_redirects=allow_redirects, timeout=timeout)2597 return status_code2598 def assert_link_status_code_is_not_404(self, link):2599 status_code = str(self.get_link_status_code(link))2600 bad_link_str = 'Error: "%s" returned a 404!' % link2601 self.assertNotEqual(status_code, "404", bad_link_str)2602 def __get_link_if_404_error(self, link):2603 status_code = str(self.get_link_status_code(link))2604 if status_code == "404":2605 return link2606 else:2607 return None2608 def assert_no_404_errors(self, multithreaded=True):2609 """ Assert no 404 errors from page links obtained from:2610 "a"->"href", "img"->"src", "link"->"href", and "script"->"src".2611 (A 404 error represents a broken link on a web page.) """2612 all_links = self.get_unique_links()2613 links = []2614 for link in all_links:2615 if "javascript:" not in link and "mailto:" not in link:2616 links.append(link)2617 broken_links = []2618 if multithreaded:2619 from multiprocessing.dummy import Pool as ThreadPool2620 pool = ThreadPool(10)2621 results = pool.map(self.__get_link_if_404_error, links)2622 pool.close()2623 pool.join()2624 for result in results:2625 if result:2626 broken_links.append(result)2627 else:2628 broken_links = []2629 for link in links:2630 if self.__get_link_if_404_error(link):2631 broken_links.append(link)2632 if len(broken_links) > 0:2633 bad_links_str = "\n".join(broken_links)2634 if len(broken_links) == 1:2635 self.fail("Broken link detected:\n%s" % bad_links_str)2636 elif len(broken_links) > 1:2637 self.fail("Broken links detected:\n%s" % bad_links_str)2638 if self.demo_mode:2639 a_t = "ASSERT NO 404 ERRORS"2640 if self._language != "English":2641 from seleniumbase.fixtures.words import SD2642 a_t = SD.translate_assert_no_404_errors(self._language)2643 messenger_post = ("%s" % a_t)2644 self.__highlight_with_assert_success(messenger_post, "html")2645 def print_unique_links_with_status_codes(self):2646 """ Finds all unique links in the html of the page source2647 and then prints out those links with their status codes.2648 Format: ["link" -> "status_code"] (per line)2649 Page links include those obtained from:2650 "a"->"href", "img"->"src", "link"->"href", and "script"->"src". """2651 page_url = self.get_current_url()2652 soup = self.get_beautiful_soup(self.get_page_source())2653 page_utils._print_unique_links_with_status_codes(page_url, soup)2654 def __fix_unicode_conversion(self, text):2655 """ Fixing Chinese characters when converting from PDF to HTML. """2656 if sys.version_info[0] < 3:2657 # Update encoding for Python 2 users2658 reload(sys) # noqa2659 sys.setdefaultencoding('utf8')2660 text = text.replace(u'\u2f8f', u'\u884c')2661 text = text.replace(u'\u2f45', u'\u65b9')2662 text = text.replace(u'\u2f08', u'\u4eba')2663 text = text.replace(u'\u2f70', u'\u793a')2664 return text2665 def get_pdf_text(self, pdf, page=None, maxpages=None,2666 password=None, codec='utf-8', wrap=False, nav=False,2667 override=False):2668 """ Gets text from a PDF file.2669 PDF can be either a URL or a file path on the local file system.2670 @Params2671 pdf - The URL or file path of the PDF file.2672 page - The page number (or a list of page numbers) of the PDF.2673 If a page number is provided, looks only at that page.2674 (1 is the first page, 2 is the second page, etc.)2675 If no page number is provided, returns all PDF text.2676 maxpages - Instead of providing a page number, you can provide2677 the number of pages to use from the beginning.2678 password - If the PDF is password-protected, enter it here.2679 codec - The compression format for character encoding.2680 (The default codec used by this method is 'utf-8'.)2681 wrap - Replaces ' \n' with ' ' so that individual sentences2682 from a PDF don't get broken up into separate lines when2683 getting converted into text format.2684 nav - If PDF is a URL, navigates to the URL in the browser first.2685 (Not needed because the PDF will be downloaded anyway.)2686 override - If the PDF file to be downloaded already exists in the2687 downloaded_files/ folder, that PDF will be used2688 instead of downloading it again. """2689 import warnings2690 with warnings.catch_warnings():2691 warnings.simplefilter("ignore", category=UserWarning)2692 from pdfminer.high_level import extract_text2693 if not password:2694 password = ''2695 if not maxpages:2696 maxpages = 02697 if not pdf.lower().endswith('.pdf'):2698 raise Exception("%s is not a PDF file! (Expecting a .pdf)" % pdf)2699 file_path = None2700 if page_utils.is_valid_url(pdf):2701 from seleniumbase.core import download_helper2702 downloads_folder = download_helper.get_downloads_folder()2703 if nav:2704 if self.get_current_url() != pdf:2705 self.open(pdf)2706 file_name = pdf.split('/')[-1]2707 file_path = downloads_folder + '/' + file_name2708 if not os.path.exists(file_path):2709 self.download_file(pdf)2710 elif override:2711 self.download_file(pdf)2712 else:2713 if not os.path.exists(pdf):2714 raise Exception("%s is not a valid URL or file path!" % pdf)2715 file_path = os.path.abspath(pdf)2716 page_search = None # (Pages are delimited by '\x0c')2717 if type(page) is list:2718 pages = page2719 page_search = []2720 for page in pages:2721 page_search.append(page - 1)2722 elif type(page) is int:2723 page = page - 12724 if page < 0:2725 page = 02726 page_search = [page]2727 else:2728 page_search = None2729 pdf_text = extract_text(2730 file_path, password='', page_numbers=page_search,2731 maxpages=maxpages, caching=False, codec=codec)2732 pdf_text = self.__fix_unicode_conversion(pdf_text)2733 if wrap:2734 pdf_text = pdf_text.replace(' \n', ' ')2735 pdf_text = pdf_text.strip() # Remove leading and trailing whitespace2736 return pdf_text2737 def assert_pdf_text(self, pdf, text, page=None, maxpages=None,2738 password=None, codec='utf-8', wrap=True, nav=False,2739 override=False):2740 """ Asserts text in a PDF file.2741 PDF can be either a URL or a file path on the local file system.2742 @Params2743 pdf - The URL or file path of the PDF file.2744 text - The expected text to verify in the PDF.2745 page - The page number of the PDF to use (optional).2746 If a page number is provided, looks only at that page.2747 (1 is the first page, 2 is the second page, etc.)2748 If no page number is provided, looks at all the pages.2749 maxpages - Instead of providing a page number, you can provide2750 the number of pages to use from the beginning.2751 password - If the PDF is password-protected, enter it here.2752 codec - The compression format for character encoding.2753 (The default codec used by this method is 'utf-8'.)2754 wrap - Replaces ' \n' with ' ' so that individual sentences2755 from a PDF don't get broken up into separate lines when2756 getting converted into text format.2757 nav - If PDF is a URL, navigates to the URL in the browser first.2758 (Not needed because the PDF will be downloaded anyway.)2759 override - If the PDF file to be downloaded already exists in the2760 downloaded_files/ folder, that PDF will be used2761 instead of downloading it again. """2762 text = self.__fix_unicode_conversion(text)2763 if not codec:2764 codec = 'utf-8'2765 pdf_text = self.get_pdf_text(2766 pdf, page=page, maxpages=maxpages, password=password, codec=codec,2767 wrap=wrap, nav=nav, override=override)2768 if type(page) is int:2769 if text not in pdf_text:2770 raise Exception("PDF [%s] is missing expected text [%s] on "2771 "page [%s]!" % (pdf, text, page))2772 else:2773 if text not in pdf_text:2774 raise Exception("PDF [%s] is missing expected text [%s]!"2775 "" % (pdf, text))2776 return True2777 def create_folder(self, folder):2778 """ Creates a folder of the given name if it doesn't already exist. """2779 if folder.endswith("/"):2780 folder = folder[:-1]2781 if len(folder) < 1:2782 raise Exception("Minimum folder name length = 1.")2783 if not os.path.exists(folder):2784 try:2785 os.makedirs(folder)2786 except Exception:2787 pass2788 def choose_file(self, selector, file_path, by=By.CSS_SELECTOR,2789 timeout=None):2790 """ This method is used to choose a file to upload to a website.2791 It works by populating a file-chooser "input" field of type="file".2792 A relative file_path will get converted into an absolute file_path.2793 Example usage:2794 self.choose_file('input[type="file"]', "my_dir/my_file.txt")2795 """2796 self.__check_scope()2797 if not timeout:2798 timeout = settings.LARGE_TIMEOUT2799 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2800 timeout = self.__get_new_timeout(timeout)2801 selector, by = self.__recalculate_selector(selector, by)2802 abs_path = os.path.abspath(file_path)2803 self.add_text(selector, abs_path, by=by, timeout=timeout)2804 def save_element_as_image_file(2805 self, selector, file_name, folder=None, overlay_text=""):2806 """ Take a screenshot of an element and save it as an image file.2807 If no folder is specified, will save it to the current folder.2808 If overlay_text is provided, will add that to the saved image. """2809 element = self.wait_for_element_visible(selector)2810 element_png = element.screenshot_as_png2811 if len(file_name.split('.')[0]) < 1:2812 raise Exception("Error: file_name length must be > 0.")2813 if not file_name.endswith(".png"):2814 file_name = file_name + ".png"2815 image_file_path = None2816 if folder:2817 if folder.endswith("/"):2818 folder = folder[:-1]2819 if len(folder) > 0:2820 self.create_folder(folder)2821 image_file_path = "%s/%s" % (folder, file_name)2822 if not image_file_path:2823 image_file_path = file_name2824 with open(image_file_path, "wb") as file:2825 file.write(element_png)2826 # Add a text overlay if given2827 if type(overlay_text) is str and len(overlay_text) > 0:2828 from PIL import Image, ImageDraw2829 text_rows = overlay_text.split("\n")2830 len_text_rows = len(text_rows)2831 max_width = 02832 for text_row in text_rows:2833 if len(text_row) > max_width:2834 max_width = len(text_row)2835 image = Image.open(image_file_path)2836 draw = ImageDraw.Draw(image)2837 draw.rectangle(2838 (0, 0, (max_width * 6) + 6, 16 * len_text_rows),2839 fill=(236, 236, 28))2840 draw.text(2841 (4, 2), # Coordinates2842 overlay_text, # Text2843 (8, 38, 176) # Color2844 )2845 image.save(image_file_path, 'PNG', quality=100, optimize=True)2846 def download_file(self, file_url, destination_folder=None):2847 """ Downloads the file from the url to the destination folder.2848 If no destination folder is specified, the default one is used.2849 (The default [Downloads Folder] = "./downloaded_files") """2850 if not destination_folder:2851 destination_folder = constants.Files.DOWNLOADS_FOLDER2852 if not os.path.exists(destination_folder):2853 os.makedirs(destination_folder)2854 page_utils._download_file_to(file_url, destination_folder)2855 def save_file_as(self, file_url, new_file_name, destination_folder=None):2856 """ Similar to self.download_file(), except that you get to rename the2857 file being downloaded to whatever you want. """2858 if not destination_folder:2859 destination_folder = constants.Files.DOWNLOADS_FOLDER2860 page_utils._download_file_to(2861 file_url, destination_folder, new_file_name)2862 def save_data_as(self, data, file_name, destination_folder=None):2863 """ Saves the data specified to a file of the name specified.2864 If no destination folder is specified, the default one is used.2865 (The default [Downloads Folder] = "./downloaded_files") """2866 if not destination_folder:2867 destination_folder = constants.Files.DOWNLOADS_FOLDER2868 page_utils._save_data_as(data, destination_folder, file_name)2869 def get_downloads_folder(self):2870 """ Returns the path of the SeleniumBase "downloaded_files/" folder.2871 Calling self.download_file(file_url) will put that file in here.2872 With the exception of Safari, IE, and Chromium Guest Mode,2873 any clicks that download files will also use this folder2874 rather than using the browser's default "downloads/" path. """2875 self.__check_scope()2876 if self.is_chromium() and self.guest_mode and not self.headless:2877 # Guest Mode (non-headless) can force the default downloads path2878 return os.path.join(os.path.expanduser('~'), 'downloads')2879 else:2880 from seleniumbase.core import download_helper2881 return download_helper.get_downloads_folder()2882 def get_browser_downloads_folder(self):2883 """ Returns the path that is used when a click initiates a download.2884 SeleniumBase overrides the system path to be "downloaded_files/"2885 The path can't be changed on Safari, IE, or Chromium Guest Mode.2886 """2887 self.__check_scope()2888 if self.is_chromium() and self.guest_mode and not self.headless:2889 # Guest Mode (non-headless) can force the default downloads path2890 return os.path.join(os.path.expanduser('~'), 'downloads')2891 elif self.browser == "safari" or self.browser == "ie":2892 # Can't change the system [Downloads Folder] on Safari or IE2893 return os.path.join(os.path.expanduser('~'), 'downloads')2894 else:2895 from seleniumbase.core import download_helper2896 return download_helper.get_downloads_folder()2897 return os.path.join(os.path.expanduser('~'), 'downloads')2898 def get_path_of_downloaded_file(self, file, browser=False):2899 """ Returns the OS path of the downloaded file. """2900 if browser:2901 return os.path.join(self.get_browser_downloads_folder(), file)2902 else:2903 return os.path.join(self.get_downloads_folder(), file)2904 def is_downloaded_file_present(self, file, browser=False):2905 """ Returns True if the file exists in the pre-set [Downloads Folder].2906 For browser click-initiated downloads, SeleniumBase will override2907 the system [Downloads Folder] to be "./downloaded_files/",2908 but that path can't be overridden when using Safari, IE,2909 or Chromium Guest Mode, which keeps the default system path.2910 self.download_file(file_url) will always use "./downloaded_files/".2911 @Params2912 file - The filename of the downloaded file.2913 browser - If True, uses the path set by click-initiated downloads.2914 If False, uses the self.download_file(file_url) path.2915 Those paths are often the same. (browser-dependent)2916 (Default: False).2917 """2918 return os.path.exists(self.get_path_of_downloaded_file(2919 file, browser=browser))2920 def assert_downloaded_file(self, file, timeout=None, browser=False):2921 """ Asserts that the file exists in SeleniumBase's [Downloads Folder].2922 For browser click-initiated downloads, SeleniumBase will override2923 the system [Downloads Folder] to be "./downloaded_files/",2924 but that path can't be overridden when using Safari, IE,2925 or Chromium Guest Mode, which keeps the default system path.2926 self.download_file(file_url) will always use "./downloaded_files/".2927 @Params2928 file - The filename of the downloaded file.2929 timeout - The time (seconds) to wait for the download to complete.2930 browser - If True, uses the path set by click-initiated downloads.2931 If False, uses the self.download_file(file_url) path.2932 Those paths are often the same. (browser-dependent)2933 (Default: False).2934 """2935 self.__check_scope()2936 if not timeout:2937 timeout = settings.LARGE_TIMEOUT2938 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2939 timeout = self.__get_new_timeout(timeout)2940 start_ms = time.time() * 1000.02941 stop_ms = start_ms + (timeout * 1000.0)2942 downloaded_file_path = self.get_path_of_downloaded_file(file, browser)2943 for x in range(int(timeout)):2944 shared_utils.check_if_time_limit_exceeded()2945 try:2946 self.assertTrue(2947 os.path.exists(downloaded_file_path),2948 "File [%s] was not found in the downloads folder [%s]!"2949 "" % (file, self.get_downloads_folder()))2950 if self.demo_mode:2951 messenger_post = ("ASSERT DOWNLOADED FILE: [%s]" % file)2952 js_utils.post_messenger_success_message(2953 self.driver, messenger_post, self.message_duration)2954 return2955 except Exception:2956 now_ms = time.time() * 1000.02957 if now_ms >= stop_ms:2958 break2959 time.sleep(1)2960 if not os.path.exists(downloaded_file_path):2961 message = (2962 "File {%s} was not found in the downloads folder {%s} "2963 "after %s seconds! (Or the download didn't complete!)"2964 "" % (file, self.get_downloads_folder(), timeout))2965 page_actions.timeout_exception("NoSuchFileException", message)2966 if self.demo_mode:2967 messenger_post = ("ASSERT DOWNLOADED FILE: [%s]" % file)2968 js_utils.post_messenger_success_message(2969 self.driver, messenger_post, self.message_duration)2970 def assert_true(self, expr, msg=None):2971 """ Asserts that the expression is True.2972 Will raise an exception if the statement if False. """2973 self.assertTrue(expr, msg=msg)2974 def assert_false(self, expr, msg=None):2975 """ Asserts that the expression is False.2976 Will raise an exception if the statement if True. """2977 self.assertFalse(expr, msg=msg)2978 def assert_equal(self, first, second, msg=None):2979 """ Asserts that the two values are equal.2980 Will raise an exception if the values are not equal. """2981 self.assertEqual(first, second, msg=msg)2982 def assert_not_equal(self, first, second, msg=None):2983 """ Asserts that the two values are not equal.2984 Will raise an exception if the values are equal. """2985 self.assertNotEqual(first, second, msg=msg)2986 def assert_raises(self, *args, **kwargs):2987 """ Asserts that the following block of code raises an exception.2988 Will raise an exception if the block of code has no exception.2989 Usage Example =>2990 # Verify that the expected exception is raised.2991 with self.assert_raises(Exception):2992 raise Exception("Expected Exception!")2993 """2994 self.assertRaises(*args, **kwargs)2995 def assert_title(self, title):2996 """ Asserts that the web page title matches the expected title.2997 When a web page initially loads, the title starts as the URL,2998 but then the title switches over to the actual page title.2999 A slow connection could delay the actual title from displaying. """3000 self.wait_for_ready_state_complete()3001 expected = title.strip()3002 actual = self.get_page_title().strip()3003 error = (3004 "Expected page title [%s] does not match the actual title [%s]!")3005 try:3006 self.assertEqual(expected, actual, error % (expected, actual))3007 except Exception:3008 self.wait_for_ready_state_complete()3009 self.sleep(settings.MINI_TIMEOUT)3010 actual = self.get_page_title().strip()3011 try:3012 self.assertEqual(expected, actual, error % (expected, actual))3013 except Exception:3014 self.wait_for_ready_state_complete()3015 self.sleep(settings.MINI_TIMEOUT)3016 actual = self.get_page_title().strip()3017 self.assertEqual(expected, actual, error % (expected, actual))3018 if self.demo_mode:3019 a_t = "ASSERT TITLE"3020 if self._language != "English":3021 from seleniumbase.fixtures.words import SD3022 a_t = SD.translate_assert_title(self._language)3023 messenger_post = ("%s: {%s}" % (a_t, title))3024 self.__highlight_with_assert_success(messenger_post, "html")3025 def assert_no_js_errors(self):3026 """ Asserts that there are no JavaScript "SEVERE"-level page errors.3027 Works ONLY for Chrome (non-headless) and Chrome-based browsers.3028 Does NOT work on Firefox, Edge, IE, and some other browsers:3029 * See https://github.com/SeleniumHQ/selenium/issues/11613030 Based on the following Stack Overflow solution:3031 * https://stackoverflow.com/a/41150512/7058266 """3032 self.__check_scope()3033 time.sleep(0.1) # May take a moment for errors to appear after loads.3034 try:3035 browser_logs = self.driver.get_log('browser')3036 except (ValueError, WebDriverException):3037 # If unable to get browser logs, skip the assert and return.3038 return3039 messenger_library = "//cdnjs.cloudflare.com/ajax/libs/messenger"3040 errors = []3041 for entry in browser_logs:3042 if entry['level'] == 'SEVERE':3043 if messenger_library not in entry['message']:3044 # Add errors if not caused by SeleniumBase dependencies3045 errors.append(entry)3046 if len(errors) > 0:3047 current_url = self.get_current_url()3048 raise Exception(3049 "JavaScript errors found on %s => %s" % (current_url, errors))3050 if self.demo_mode:3051 if (self.browser == "chrome" or self.browser == "edge"):3052 a_t = "ASSERT NO JS ERRORS"3053 if self._language != "English":3054 from seleniumbase.fixtures.words import SD3055 a_t = SD.translate_assert_no_js_errors(self._language)3056 messenger_post = ("%s" % a_t)3057 self.__highlight_with_assert_success(messenger_post, "html")3058 def __activate_html_inspector(self):3059 self.wait_for_ready_state_complete()3060 time.sleep(0.05)3061 js_utils.activate_html_inspector(self.driver)3062 def inspect_html(self):3063 """ Inspects the Page HTML with HTML-Inspector.3064 (https://github.com/philipwalton/html-inspector)3065 (https://cdnjs.com/libraries/html-inspector)3066 Prints the results and also returns them. """3067 self.__activate_html_inspector()3068 script = ("""HTMLInspector.inspect();""")3069 self.execute_script(script)3070 time.sleep(0.1)3071 browser_logs = []3072 try:3073 browser_logs = self.driver.get_log('browser')3074 except (ValueError, WebDriverException):3075 # If unable to get browser logs, skip the assert and return.3076 return("(Unable to Inspect HTML! -> Only works on Chrome!)")3077 messenger_library = "//cdnjs.cloudflare.com/ajax/libs/messenger"3078 url = self.get_current_url()3079 header = '\n* HTML Inspection Results: %s' % url3080 results = [header]3081 row_count = 03082 for entry in browser_logs:3083 message = entry['message']3084 if "0:6053 " in message:3085 message = message.split("0:6053")[1]3086 message = message.replace("\\u003C", "<")3087 if message.startswith(' "') and message.count('"') == 2:3088 message = message.split('"')[1]3089 message = "X - " + message3090 if messenger_library not in message:3091 if message not in results:3092 results.append(message)3093 row_count += 13094 if row_count > 0:3095 results.append('* (See the Console output for details!)')3096 else:3097 results.append('* (No issues detected!)')3098 results = '\n'.join(results)3099 print(results)3100 return(results)3101 def is_chromium(self):3102 """ Return True if the browser is Chrome, Edge, or Opera. """3103 self.__check_scope()3104 chromium = False3105 browser_name = self.driver.__dict__["capabilities"]["browserName"]3106 if browser_name in ("chrome", "edge", "msedge", "opera"):3107 chromium = True3108 return chromium3109 def get_google_auth_password(self, totp_key=None):3110 """ Returns a time-based one-time password based on the3111 Google Authenticator password algorithm. Works with Authy.3112 If "totp_key" is not specified, defaults to using the one3113 provided in seleniumbase/config/settings.py3114 Google Auth passwords expire and change at 30-second intervals.3115 If the fetched password expires in the next 1.5 seconds, waits3116 for a new one before returning it (may take up to 1.5 seconds).3117 See https://pyotp.readthedocs.io/en/latest/ for details. """3118 import pyotp3119 if not totp_key:3120 totp_key = settings.TOTP_KEY3121 epoch_interval = time.time() / 30.03122 cycle_lifespan = float(epoch_interval) - int(epoch_interval)3123 if float(cycle_lifespan) > 0.95:3124 # Password expires in the next 1.5 seconds. Wait for a new one.3125 for i in range(30):3126 time.sleep(0.05)3127 epoch_interval = time.time() / 30.03128 cycle_lifespan = float(epoch_interval) - int(epoch_interval)3129 if not float(cycle_lifespan) > 0.95:3130 # The new password cycle has begun3131 break3132 totp = pyotp.TOTP(totp_key)3133 return str(totp.now())3134 def convert_css_to_xpath(self, css):3135 return css_to_xpath.convert_css_to_xpath(css)3136 def convert_xpath_to_css(self, xpath):3137 return xpath_to_css.convert_xpath_to_css(xpath)3138 def convert_to_css_selector(self, selector, by):3139 """ This method converts a selector to a CSS_SELECTOR.3140 jQuery commands require a CSS_SELECTOR for finding elements.3141 This method should only be used for jQuery/JavaScript actions.3142 Pure JavaScript doesn't support using a:contains("LINK_TEXT"). """3143 if by == By.CSS_SELECTOR:3144 return selector3145 elif by == By.ID:3146 return '#%s' % selector3147 elif by == By.CLASS_NAME:3148 return '.%s' % selector3149 elif by == By.NAME:3150 return '[name="%s"]' % selector3151 elif by == By.TAG_NAME:3152 return selector3153 elif by == By.XPATH:3154 return self.convert_xpath_to_css(selector)3155 elif by == By.LINK_TEXT:3156 return 'a:contains("%s")' % selector3157 elif by == By.PARTIAL_LINK_TEXT:3158 return 'a:contains("%s")' % selector3159 else:3160 raise Exception(3161 "Exception: Could not convert {%s}(by=%s) to CSS_SELECTOR!" % (3162 selector, by))3163 def set_value(self, selector, text, by=By.CSS_SELECTOR, timeout=None):3164 """ This method uses JavaScript to update a text field. """3165 self.__check_scope()3166 if not timeout:3167 timeout = settings.LARGE_TIMEOUT3168 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3169 timeout = self.__get_new_timeout(timeout)3170 selector, by = self.__recalculate_selector(selector, by, xp_ok=False)3171 orginal_selector = selector3172 css_selector = self.convert_to_css_selector(selector, by=by)3173 self.__demo_mode_highlight_if_active(orginal_selector, by)3174 if not self.demo_mode and not self.slow_mode:3175 self.scroll_to(orginal_selector, by=by, timeout=timeout)3176 if type(text) is int or type(text) is float:3177 text = str(text)3178 value = re.escape(text)3179 value = self.__escape_quotes_if_needed(value)3180 css_selector = re.escape(css_selector) # Add "\\" to special chars3181 css_selector = self.__escape_quotes_if_needed(css_selector)3182 if ":contains\\(" not in css_selector:3183 script = (3184 """document.querySelector('%s').value='%s';"""3185 "" % (css_selector, value))3186 self.execute_script(script)3187 else:3188 script = (3189 """jQuery('%s')[0].value='%s';"""3190 "" % (css_selector, value))3191 self.safe_execute_script(script)3192 if text.endswith('\n'):3193 element = self.wait_for_element_present(3194 orginal_selector, by=by, timeout=timeout)3195 element.send_keys(Keys.RETURN)3196 if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:3197 self.wait_for_ready_state_complete()3198 self.__demo_mode_pause_if_active()3199 def js_update_text(self, selector, text, by=By.CSS_SELECTOR,3200 timeout=None):3201 """ JavaScript + send_keys are used to update a text field.3202 Performs self.set_value() and triggers event listeners.3203 If text ends in "\n", set_value() presses RETURN after.3204 Works faster than send_keys() alone due to the JS call.3205 """3206 self.__check_scope()3207 if not timeout:3208 timeout = settings.LARGE_TIMEOUT3209 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3210 timeout = self.__get_new_timeout(timeout)3211 selector, by = self.__recalculate_selector(selector, by)3212 if type(text) is int or type(text) is float:3213 text = str(text)3214 self.set_value(3215 selector, text, by=by, timeout=timeout)3216 if not text.endswith('\n'):3217 try:3218 element = page_actions.wait_for_element_present(3219 self.driver, selector, by, timeout=0.2)3220 element.send_keys(" \b")3221 except Exception:3222 pass3223 def js_type(self, selector, text, by=By.CSS_SELECTOR,3224 timeout=None):3225 """ Same as self.js_update_text()3226 JavaScript + send_keys are used to update a text field.3227 Performs self.set_value() and triggers event listeners.3228 If text ends in "\n", set_value() presses RETURN after.3229 Works faster than send_keys() alone due to the JS call.3230 """3231 self.__check_scope()3232 if not timeout:3233 timeout = settings.LARGE_TIMEOUT3234 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3235 timeout = self.__get_new_timeout(timeout)3236 selector, by = self.__recalculate_selector(selector, by)3237 if type(text) is int or type(text) is float:3238 text = str(text)3239 self.set_value(3240 selector, text, by=by, timeout=timeout)3241 if not text.endswith('\n'):3242 try:3243 element = page_actions.wait_for_element_present(3244 self.driver, selector, by, timeout=0.2)3245 element.send_keys(" \b")3246 except Exception:3247 pass3248 def set_text(self, selector, text, by=By.CSS_SELECTOR, timeout=None):3249 """ Same as self.js_update_text()3250 JavaScript + send_keys are used to update a text field.3251 Performs self.set_value() and triggers event listeners.3252 If text ends in "\n", set_value() presses RETURN after.3253 Works faster than send_keys() alone due to the JS call. """3254 self.__check_scope()3255 if not timeout:3256 timeout = settings.LARGE_TIMEOUT3257 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3258 timeout = self.__get_new_timeout(timeout)3259 selector, by = self.__recalculate_selector(selector, by)3260 if type(text) is int or type(text) is float:3261 text = str(text)3262 self.set_value(3263 selector, text, by=by, timeout=timeout)3264 if not text.endswith('\n'):3265 try:3266 element = page_actions.wait_for_element_present(3267 self.driver, selector, by, timeout=0.2)3268 element.send_keys(" \b")3269 except Exception:3270 pass3271 def jquery_update_text(self, selector, text, by=By.CSS_SELECTOR,3272 timeout=None):3273 """ This method uses jQuery to update a text field.3274 If the text string ends with the newline character,3275 Selenium finishes the call, which simulates pressing3276 {Enter/Return} after the text is entered. """3277 self.__check_scope()3278 if not timeout:3279 timeout = settings.LARGE_TIMEOUT3280 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3281 timeout = self.__get_new_timeout(timeout)3282 selector, by = self.__recalculate_selector(selector, by, xp_ok=False)3283 element = self.wait_for_element_visible(3284 selector, by=by, timeout=timeout)3285 self.__demo_mode_highlight_if_active(selector, by)3286 self.scroll_to(selector, by=by)3287 selector = self.convert_to_css_selector(selector, by=by)3288 selector = self.__make_css_match_first_element_only(selector)3289 selector = self.__escape_quotes_if_needed(selector)3290 text = re.escape(text)3291 text = self.__escape_quotes_if_needed(text)3292 update_text_script = """jQuery('%s').val('%s');""" % (3293 selector, text)3294 self.safe_execute_script(update_text_script)3295 if text.endswith('\n'):3296 element.send_keys('\n')3297 self.__demo_mode_pause_if_active()3298 def set_time_limit(self, time_limit):3299 self.__check_scope()3300 if time_limit:3301 try:3302 sb_config.time_limit = float(time_limit)3303 except Exception:3304 sb_config.time_limit = None3305 else:3306 sb_config.time_limit = None3307 if sb_config.time_limit and sb_config.time_limit > 0:3308 sb_config.time_limit_ms = int(sb_config.time_limit * 1000.0)3309 self.time_limit = sb_config.time_limit3310 else:3311 self.time_limit = None3312 sb_config.time_limit = None3313 sb_config.time_limit_ms = None3314 def skip(self, reason=""):3315 """ Mark the test as Skipped. """3316 self.__check_scope()3317 if self.dashboard or (self.is_pytest and self.with_db_reporting):3318 test_id = self.__get_test_id_2()3319 sb_config._results[test_id] = "Skipped"3320 self.__skip_reason = reason3321 elif reason and not self.is_pytest:3322 # Only needed for nosetest db reporting3323 self._nose_skip_reason = reason3324 self.skipTest(reason)3325 ############3326 # Application "Local Storage" controls3327 def set_local_storage_item(self, key, value):3328 self.__check_scope()3329 self.execute_script(3330 "window.localStorage.setItem('{}', '{}');".format(key, value))3331 def get_local_storage_item(self, key):3332 self.__check_scope()3333 return self.execute_script(3334 "return window.localStorage.getItem('{}');".format(key))3335 def remove_local_storage_item(self, key):3336 self.__check_scope()3337 self.execute_script(3338 "window.localStorage.removeItem('{}');".format(key))3339 def clear_local_storage(self):3340 self.__check_scope()3341 self.execute_script("window.localStorage.clear();")3342 def get_local_storage_keys(self):3343 self.__check_scope()3344 return self.execute_script(3345 "var ls = window.localStorage, keys = []; "3346 "for (var i = 0; i < ls.length; ++i) "3347 " keys[i] = ls.key(i); "3348 "return keys;")3349 def get_local_storage_items(self):3350 self.__check_scope()3351 return self.execute_script(3352 r"var ls = window.localStorage, items = {}; "3353 "for (var i = 0, k; i < ls.length; ++i) "3354 " items[k = ls.key(i)] = ls.getItem(k); "3355 "return items;")3356 # Application "Session Storage" controls3357 def set_session_storage_item(self, key, value):3358 self.__check_scope()3359 self.execute_script(3360 "window.sessionStorage.setItem('{}', '{}');".format(key, value))3361 def get_session_storage_item(self, key):3362 self.__check_scope()3363 return self.execute_script(3364 "return window.sessionStorage.getItem('{}');".format(key))3365 def remove_session_storage_item(self, key):3366 self.__check_scope()3367 self.execute_script(3368 "window.sessionStorage.removeItem('{}');".format(key))3369 def clear_session_storage(self):3370 self.__check_scope()3371 self.execute_script("window.sessionStorage.clear();")3372 def get_session_storage_keys(self):3373 self.__check_scope()3374 return self.execute_script(3375 "var ls = window.sessionStorage, keys = []; "3376 "for (var i = 0; i < ls.length; ++i) "3377 " keys[i] = ls.key(i); "3378 "return keys;")3379 def get_session_storage_items(self):3380 self.__check_scope()3381 return self.execute_script(3382 r"var ls = window.sessionStorage, items = {}; "3383 "for (var i = 0, k; i < ls.length; ++i) "3384 " items[k = ls.key(i)] = ls.getItem(k); "3385 "return items;")3386 ############3387 # Duplicates (Avoids name confusion when migrating from other frameworks.)3388 def open_url(self, url):3389 """ Same as self.open() """3390 self.open(url)3391 def visit(self, url):3392 """ Same as self.open() """3393 self.open(url)3394 def visit_url(self, url):3395 """ Same as self.open() """3396 self.open(url)3397 def goto(self, url):3398 """ Same as self.open() """3399 self.open(url)3400 def go_to(self, url):3401 """ Same as self.open() """3402 self.open(url)3403 def reload(self):3404 """ Same as self.refresh_page() """3405 self.refresh_page()3406 def reload_page(self):3407 """ Same as self.refresh_page() """3408 self.refresh_page()3409 def input(self, selector, text, by=By.CSS_SELECTOR,3410 timeout=None, retry=False):3411 """ Same as self.update_text() """3412 self.__check_scope()3413 if not timeout:3414 timeout = settings.LARGE_TIMEOUT3415 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3416 timeout = self.__get_new_timeout(timeout)3417 selector, by = self.__recalculate_selector(selector, by)3418 self.update_text(selector, text, by=by, timeout=timeout, retry=retry)3419 def write(self, selector, text, by=By.CSS_SELECTOR,3420 timeout=None, retry=False):3421 """ Same as self.update_text() """3422 self.__check_scope()3423 if not timeout:3424 timeout = settings.LARGE_TIMEOUT3425 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3426 timeout = self.__get_new_timeout(timeout)3427 selector, by = self.__recalculate_selector(selector, by)3428 self.update_text(selector, text, by=by, timeout=timeout, retry=retry)3429 def send_keys(self, selector, text, by=By.CSS_SELECTOR, timeout=None):3430 """ Same as self.add_text() """3431 self.__check_scope()3432 if not timeout:3433 timeout = settings.LARGE_TIMEOUT3434 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3435 timeout = self.__get_new_timeout(timeout)3436 selector, by = self.__recalculate_selector(selector, by)3437 self.add_text(selector, text, by=by, timeout=timeout)3438 def click_link(self, link_text, timeout=None):3439 """ Same as self.click_link_text() """3440 self.__check_scope()3441 if not timeout:3442 timeout = settings.SMALL_TIMEOUT3443 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3444 timeout = self.__get_new_timeout(timeout)3445 self.click_link_text(link_text, timeout=timeout)3446 def click_partial_link(self, partial_link_text, timeout=None):3447 """ Same as self.click_partial_link_text() """3448 self.__check_scope()3449 if not timeout:3450 timeout = settings.SMALL_TIMEOUT3451 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3452 timeout = self.__get_new_timeout(timeout)3453 self.click_partial_link_text(partial_link_text, timeout=timeout)3454 def wait_for_element_visible(self, selector, by=By.CSS_SELECTOR,3455 timeout=None):3456 """ Same as self.wait_for_element() """3457 self.__check_scope()3458 if not timeout:3459 timeout = settings.LARGE_TIMEOUT3460 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3461 timeout = self.__get_new_timeout(timeout)3462 selector, by = self.__recalculate_selector(selector, by)3463 return page_actions.wait_for_element_visible(3464 self.driver, selector, by, timeout)3465 def wait_for_element_not_present(self, selector, by=By.CSS_SELECTOR,3466 timeout=None):3467 """ Same as self.wait_for_element_absent()3468 Waits for an element to no longer appear in the HTML of a page.3469 A hidden element still counts as appearing in the page HTML.3470 If an element with "hidden" status is acceptable,3471 use wait_for_element_not_visible() instead. """3472 self.__check_scope()3473 if not timeout:3474 timeout = settings.LARGE_TIMEOUT3475 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3476 timeout = self.__get_new_timeout(timeout)3477 selector, by = self.__recalculate_selector(selector, by)3478 return page_actions.wait_for_element_absent(3479 self.driver, selector, by, timeout)3480 def assert_element_not_present(self, selector, by=By.CSS_SELECTOR,3481 timeout=None):3482 """ Same as self.assert_element_absent()3483 Will raise an exception if the element stays present.3484 Returns True if successful. Default timeout = SMALL_TIMEOUT. """3485 self.__check_scope()3486 if not timeout:3487 timeout = settings.SMALL_TIMEOUT3488 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3489 timeout = self.__get_new_timeout(timeout)3490 self.wait_for_element_absent(selector, by=by, timeout=timeout)3491 return True3492 def assert_no_broken_links(self, multithreaded=True):3493 """ Same as self.assert_no_404_errors() """3494 self.assert_no_404_errors(multithreaded=multithreaded)3495 def wait(self, seconds):3496 """ Same as self.sleep() - Some JS frameworks use this method name. """3497 self.sleep(seconds)3498 def block_ads(self):3499 """ Same as self.ad_block() """3500 self.ad_block()3501 def _print(self, msg):3502 """ Same as Python's print() """3503 print(msg)3504 def start_tour(self, name=None, interval=0):3505 self.play_tour(name=name, interval=interval)3506 ############3507 def add_css_link(self, css_link):3508 self.__check_scope()3509 js_utils.add_css_link(self.driver, css_link)3510 def add_js_link(self, js_link):3511 self.__check_scope()3512 js_utils.add_js_link(self.driver, js_link)3513 def add_css_style(self, css_style):3514 self.__check_scope()3515 js_utils.add_css_style(self.driver, css_style)3516 def add_js_code_from_link(self, js_link):3517 self.__check_scope()3518 js_utils.add_js_code_from_link(self.driver, js_link)3519 def add_js_code(self, js_code):3520 self.__check_scope()3521 js_utils.add_js_code(self.driver, js_code)3522 def add_meta_tag(self, http_equiv=None, content=None):3523 self.__check_scope()3524 js_utils.add_meta_tag(3525 self.driver, http_equiv=http_equiv, content=content)3526 ############3527 def create_presentation(3528 self, name=None, theme="default", transition="default"):3529 """ Creates a Reveal-JS presentation that you can add slides to.3530 @Params3531 name - If creating multiple presentations at the same time,3532 use this to specify the name of the current presentation.3533 theme - Set a theme with a unique style for the presentation.3534 Valid themes: "serif" (default), "sky", "white", "black",3535 "simple", "league", "moon", "night",3536 "beige", "blood", and "solarized".3537 transition - Set a transition between slides.3538 Valid transitions: "none" (default), "slide", "fade",3539 "zoom", "convex", and "concave".3540 """3541 if not name:3542 name = "default"3543 if not theme or theme == "default":3544 theme = "serif"3545 valid_themes = (["serif", "white", "black", "beige", "simple", "sky",3546 "league", "moon", "night", "blood", "solarized"])3547 theme = theme.lower()3548 if theme not in valid_themes:3549 raise Exception(3550 "Theme {%s} not found! Valid themes: %s"3551 "" % (theme, valid_themes))3552 if not transition or transition == "default":3553 transition = "none"3554 valid_transitions = (3555 ["none", "slide", "fade", "zoom", "convex", "concave"])3556 transition = transition.lower()3557 if transition not in valid_transitions:3558 raise Exception(3559 "Transition {%s} not found! Valid transitions: %s"3560 "" % (transition, valid_transitions))3561 reveal_theme_css = None3562 if theme == "serif":3563 reveal_theme_css = constants.Reveal.SERIF_MIN_CSS3564 elif theme == "sky":3565 reveal_theme_css = constants.Reveal.SKY_MIN_CSS3566 elif theme == "white":3567 reveal_theme_css = constants.Reveal.WHITE_MIN_CSS3568 elif theme == "black":3569 reveal_theme_css = constants.Reveal.BLACK_MIN_CSS3570 elif theme == "simple":3571 reveal_theme_css = constants.Reveal.SIMPLE_MIN_CSS3572 elif theme == "league":3573 reveal_theme_css = constants.Reveal.LEAGUE_MIN_CSS3574 elif theme == "moon":3575 reveal_theme_css = constants.Reveal.MOON_MIN_CSS3576 elif theme == "night":3577 reveal_theme_css = constants.Reveal.NIGHT_MIN_CSS3578 elif theme == "beige":3579 reveal_theme_css = constants.Reveal.BEIGE_MIN_CSS3580 elif theme == "blood":3581 reveal_theme_css = constants.Reveal.BLOOD_MIN_CSS3582 elif theme == "solarized":3583 reveal_theme_css = constants.Reveal.SOLARIZED_MIN_CSS3584 else:3585 # Use the default if unable to determine the theme3586 reveal_theme_css = constants.Reveal.SERIF_MIN_CSS3587 new_presentation = (3588 '<html>\n'3589 '<head>\n'3590 '<meta charset="utf-8">\n'3591 '<meta http-equiv="Content-Type" '3592 'content="text/html, charset=utf-8;">\n'3593 '<meta name="viewport" content="shrink-to-fit=no">\n'3594 '<link rel="stylesheet" href="%s">\n'3595 '<link rel="stylesheet" href="%s">\n'3596 '<style>\n'3597 'pre{background-color:#fbe8d4;border-radius:8px;}\n'3598 'div[flex_div]{height:68vh;margin:0;align-items:center;'3599 'justify-content:center;}\n'3600 'img[rounded]{border-radius:16px;max-width:64%%;}\n'3601 '</style>\n'3602 '</head>\n\n'3603 '<body>\n'3604 '<!-- Generated by SeleniumBase - https://seleniumbase.io -->\n'3605 '<div class="reveal">\n'3606 '<div class="slides">\n'3607 '' % (constants.Reveal.MIN_CSS, reveal_theme_css))3608 self._presentation_slides[name] = []3609 self._presentation_slides[name].append(new_presentation)3610 self._presentation_transition[name] = transition3611 def add_slide(self, content=None, image=None, code=None, iframe=None,3612 content2=None, notes=None, transition=None, name=None):3613 """ Allows the user to add slides to a presentation.3614 @Params3615 content - The HTML content to display on the presentation slide.3616 image - Attach an image (from a URL link) to the slide.3617 code - Attach code of any programming language to the slide.3618 Language-detection will be used to add syntax formatting.3619 iframe - Attach an iFrame (from a URL link) to the slide.3620 content2 - HTML content to display after adding an image or code.3621 notes - Additional notes to include with the slide.3622 ONLY SEEN if show_notes is set for the presentation.3623 transition - Set a transition between slides. (overrides previous)3624 Valid transitions: "none" (default), "slide", "fade",3625 "zoom", "convex", and "concave".3626 name - If creating multiple presentations at the same time,3627 use this to select the presentation to add slides to.3628 """3629 if not name:3630 name = "default"3631 if name not in self._presentation_slides:3632 # Create a presentation if it doesn't already exist3633 self.create_presentation(name=name)3634 if not content:3635 content = ""3636 if not content2:3637 content2 = ""3638 if not notes:3639 notes = ""3640 if not transition:3641 transition = self._presentation_transition[name]3642 elif transition == "default":3643 transition = "none"3644 valid_transitions = (3645 ["none", "slide", "fade", "zoom", "convex", "concave"])3646 transition = transition.lower()3647 if transition not in valid_transitions:3648 raise Exception(3649 "Transition {%s} not found! Valid transitions: %s"3650 "" % (transition, valid_transitions))3651 add_line = ""3652 if content.startswith("<"):3653 add_line = "\n"3654 html = (3655 '\n<section data-transition="%s">%s%s' % (3656 transition, add_line, content))3657 if image:3658 html += '\n<div flex_div><img rounded src="%s" /></div>' % image3659 if code:3660 html += '\n<div></div>'3661 html += '\n<pre class="prettyprint">\n%s</pre>' % code3662 if iframe:3663 html += ('\n<div></div>'3664 '\n<iframe src="%s" style="width:92%%;height:550px;" '3665 'title="iframe content"></iframe>' % iframe)3666 add_line = ""3667 if content2.startswith("<"):3668 add_line = "\n"3669 if content2:3670 html += '%s%s' % (add_line, content2)3671 html += '\n<aside class="notes">%s</aside>' % notes3672 html += '\n</section>\n'3673 self._presentation_slides[name].append(html)3674 def save_presentation(3675 self, name=None, filename=None, show_notes=False, interval=0):3676 """ Saves a Reveal-JS Presentation to a file for later use.3677 @Params3678 name - If creating multiple presentations at the same time,3679 use this to select the one you wish to use.3680 filename - The name of the HTML file that you wish to3681 save the presentation to. (filename must end in ".html")3682 show_notes - When set to True, the Notes feature becomes enabled,3683 which allows presenters to see notes next to slides.3684 interval - The delay time between autoplaying slides. (in seconds)3685 If set to 0 (default), autoplay is disabled.3686 """3687 if not name:3688 name = "default"3689 if not filename:3690 filename = "my_presentation.html"3691 if name not in self._presentation_slides:3692 raise Exception("Presentation {%s} does not exist!" % name)3693 if not filename.endswith('.html'):3694 raise Exception('Presentation file must end in ".html"!')3695 if not interval:3696 interval = 03697 if not type(interval) is int and not type(interval) is float:3698 raise Exception('Expecting a numeric value for "interval"!')3699 if interval < 0:3700 raise Exception('The "interval" cannot be a negative number!')3701 interval_ms = float(interval) * 1000.03702 show_notes_str = "false"3703 if show_notes:3704 show_notes_str = "true"3705 the_html = ""3706 for slide in self._presentation_slides[name]:3707 the_html += slide3708 the_html += (3709 '\n</div>\n'3710 '</div>\n'3711 '<script src="%s"></script>\n'3712 '<script src="%s"></script>\n'3713 '<script>Reveal.initialize('3714 '{showNotes: %s, slideNumber: true, hash: false, '3715 'autoSlide: %s,});'3716 '</script>\n'3717 '</body>\n'3718 '</html>\n'3719 '' % (constants.Reveal.MIN_JS,3720 constants.PrettifyJS.RUN_PRETTIFY_JS,3721 show_notes_str,3722 interval_ms))3723 saved_presentations_folder = constants.Presentations.SAVED_FOLDER3724 if saved_presentations_folder.endswith("/"):3725 saved_presentations_folder = saved_presentations_folder[:-1]3726 if not os.path.exists(saved_presentations_folder):3727 try:3728 os.makedirs(saved_presentations_folder)3729 except Exception:3730 pass3731 file_path = saved_presentations_folder + "/" + filename3732 out_file = codecs.open(file_path, "w+", encoding="utf-8")3733 out_file.writelines(the_html)3734 out_file.close()3735 print('\n>>> [%s] was saved!\n' % file_path)3736 return file_path3737 def begin_presentation(3738 self, name=None, filename=None, show_notes=False, interval=0):3739 """ Begin a Reveal-JS Presentation in the web browser.3740 @Params3741 name - If creating multiple presentations at the same time,3742 use this to select the one you wish to use.3743 filename - The name of the HTML file that you wish to3744 save the presentation to. (filename must end in ".html")3745 show_notes - When set to True, the Notes feature becomes enabled,3746 which allows presenters to see notes next to slides.3747 interval - The delay time between autoplaying slides. (in seconds)3748 If set to 0 (default), autoplay is disabled.3749 """3750 if self.headless:3751 return # Presentations should not run in headless mode.3752 if not name:3753 name = "default"3754 if not filename:3755 filename = "my_presentation.html"3756 if name not in self._presentation_slides:3757 raise Exception("Presentation {%s} does not exist!" % name)3758 if not filename.endswith('.html'):3759 raise Exception('Presentation file must end in ".html"!')3760 if not interval:3761 interval = 03762 if not type(interval) is int and not type(interval) is float:3763 raise Exception('Expecting a numeric value for "interval"!')3764 if interval < 0:3765 raise Exception('The "interval" cannot be a negative number!')3766 end_slide = (3767 '\n<section data-transition="none">\n'3768 '<p class="End_Presentation_Now"> </p>\n</section>\n')3769 self._presentation_slides[name].append(end_slide)3770 file_path = self.save_presentation(3771 name=name, filename=filename,3772 show_notes=show_notes, interval=interval)3773 self._presentation_slides[name].pop()3774 self.open_html_file(file_path)3775 presentation_folder = constants.Presentations.SAVED_FOLDER3776 try:3777 while (len(self.driver.window_handles) > 0 and (3778 presentation_folder in self.get_current_url())):3779 time.sleep(0.05)3780 if self.is_element_visible(3781 "section.present p.End_Presentation_Now"):3782 break3783 time.sleep(0.05)3784 except Exception:3785 pass3786 ############3787 def create_pie_chart(3788 self, chart_name=None, title=None, subtitle=None,3789 data_name=None, unit=None, libs=True):3790 """ Creates a JavaScript pie chart using "HighCharts".3791 @Params3792 chart_name - If creating multiple charts,3793 use this to select which one.3794 title - The title displayed for the chart.3795 subtitle - The subtitle displayed for the chart.3796 data_name - Set the series name. Useful for multi-series charts.3797 unit - The description label given to the chart's y-axis values.3798 libs - The option to include Chart libraries (JS and CSS files).3799 Should be set to True (default) for the first time creating3800 a chart on a web page. If creating multiple charts on the3801 same web page, you won't need to re-import the libraries3802 when creating additional charts.3803 """3804 if not chart_name:3805 chart_name = "default"3806 if not data_name:3807 data_name = ""3808 style = "pie"3809 self.__create_highchart(3810 chart_name=chart_name, title=title, subtitle=subtitle,3811 style=style, data_name=data_name, unit=unit, libs=libs)3812 def create_bar_chart(3813 self, chart_name=None, title=None, subtitle=None,3814 data_name=None, unit=None, libs=True):3815 """ Creates a JavaScript bar chart using "HighCharts".3816 @Params3817 chart_name - If creating multiple charts,3818 use this to select which one.3819 title - The title displayed for the chart.3820 subtitle - The subtitle displayed for the chart.3821 data_name - Set the series name. Useful for multi-series charts.3822 unit - The description label given to the chart's y-axis values.3823 libs - The option to include Chart libraries (JS and CSS files).3824 Should be set to True (default) for the first time creating3825 a chart on a web page. If creating multiple charts on the3826 same web page, you won't need to re-import the libraries3827 when creating additional charts.3828 """3829 if not chart_name:3830 chart_name = "default"3831 if not data_name:3832 data_name = ""3833 style = "bar"3834 self.__create_highchart(3835 chart_name=chart_name, title=title, subtitle=subtitle,3836 style=style, data_name=data_name, unit=unit, libs=libs)3837 def create_column_chart(3838 self, chart_name=None, title=None, subtitle=None,3839 data_name=None, unit=None, libs=True):3840 """ Creates a JavaScript column chart using "HighCharts".3841 @Params3842 chart_name - If creating multiple charts,3843 use this to select which one.3844 title - The title displayed for the chart.3845 subtitle - The subtitle displayed for the chart.3846 data_name - Set the series name. Useful for multi-series charts.3847 unit - The description label given to the chart's y-axis values.3848 libs - The option to include Chart libraries (JS and CSS files).3849 Should be set to True (default) for the first time creating3850 a chart on a web page. If creating multiple charts on the3851 same web page, you won't need to re-import the libraries3852 when creating additional charts.3853 """3854 if not chart_name:3855 chart_name = "default"3856 if not data_name:3857 data_name = ""3858 style = "column"3859 self.__create_highchart(3860 chart_name=chart_name, title=title, subtitle=subtitle,3861 style=style, data_name=data_name, unit=unit, libs=libs)3862 def create_line_chart(3863 self, chart_name=None, title=None, subtitle=None,3864 data_name=None, unit=None, zero=False, libs=True):3865 """ Creates a JavaScript line chart using "HighCharts".3866 @Params3867 chart_name - If creating multiple charts,3868 use this to select which one.3869 title - The title displayed for the chart.3870 subtitle - The subtitle displayed for the chart.3871 data_name - Set the series name. Useful for multi-series charts.3872 unit - The description label given to the chart's y-axis values.3873 zero - If True, the y-axis always starts at 0. (Default: False).3874 libs - The option to include Chart libraries (JS and CSS files).3875 Should be set to True (default) for the first time creating3876 a chart on a web page. If creating multiple charts on the3877 same web page, you won't need to re-import the libraries3878 when creating additional charts.3879 """3880 if not chart_name:3881 chart_name = "default"3882 if not data_name:3883 data_name = ""3884 style = "line"3885 self.__create_highchart(3886 chart_name=chart_name, title=title, subtitle=subtitle,3887 style=style, data_name=data_name, unit=unit, zero=zero, libs=libs)3888 def create_area_chart(3889 self, chart_name=None, title=None, subtitle=None,3890 data_name=None, unit=None, zero=False, libs=True):3891 """ Creates a JavaScript area chart using "HighCharts".3892 @Params3893 chart_name - If creating multiple charts,3894 use this to select which one.3895 title - The title displayed for the chart.3896 subtitle - The subtitle displayed for the chart.3897 data_name - Set the series name. Useful for multi-series charts.3898 unit - The description label given to the chart's y-axis values.3899 zero - If True, the y-axis always starts at 0. (Default: False).3900 libs - The option to include Chart libraries (JS and CSS files).3901 Should be set to True (default) for the first time creating3902 a chart on a web page. If creating multiple charts on the3903 same web page, you won't need to re-import the libraries3904 when creating additional charts.3905 """3906 if not chart_name:3907 chart_name = "default"3908 if not data_name:3909 data_name = ""3910 style = "area"3911 self.__create_highchart(3912 chart_name=chart_name, title=title, subtitle=subtitle,3913 style=style, data_name=data_name, unit=unit, zero=zero, libs=libs)3914 def __create_highchart(3915 self, chart_name=None, title=None, subtitle=None,3916 style=None, data_name=None, unit=None, zero=False, libs=True):3917 """ Creates a JavaScript chart using the "HighCharts" library. """3918 if not chart_name:3919 chart_name = "default"3920 if not title:3921 title = ""3922 if not subtitle:3923 subtitle = ""3924 if not style:3925 style = "pie"3926 if not data_name:3927 data_name = "Series 1"3928 if not unit:3929 unit = "Values"3930 title = title.replace("'", "\\'")3931 subtitle = subtitle.replace("'", "\\'")3932 unit = unit.replace("'", "\\'")3933 self._chart_count += 13934 chart_libs = (3935 """3936 <script src="%s"></script>3937 <script src="%s"></script>3938 <script src="%s"></script>3939 <script src="%s"></script>3940 <script src="%s"></script>3941 """ % (3942 constants.JQuery.MIN_JS,3943 constants.HighCharts.HC_JS,3944 constants.HighCharts.EXPORTING_JS,3945 constants.HighCharts.EXPORT_DATA_JS,3946 constants.HighCharts.ACCESSIBILITY_JS))3947 if not libs:3948 chart_libs = ""3949 chart_css = (3950 """3951 <style>3952 .highcharts-figure, .highcharts-data-table table {3953 min-width: 320px;3954 max-width: 660px;3955 margin: 1em auto;3956 }3957 .highcharts-data-table table {3958 font-family: Verdana, sans-serif;3959 border-collapse: collapse;3960 border: 1px solid #EBEBEB;3961 margin: 10px auto;3962 text-align: center;3963 width: 100%;3964 max-width: 500px;3965 }3966 .highcharts-data-table caption {3967 padding: 1em 0;3968 font-size: 1.2em;3969 color: #555;3970 }3971 .highcharts-data-table th {3972 font-weight: 600;3973 padding: 0.5em;3974 }3975 .highcharts-data-table td, .highcharts-data-table th,3976 .highcharts-data-table caption {3977 padding: 0.5em;3978 }3979 .highcharts-data-table thead tr,3980 .highcharts-data-table tr:nth-child(even) {3981 background: #f8f8f8;3982 }3983 .highcharts-data-table tr:hover {3984 background: #f1f7ff;3985 }3986 </style>3987 """)3988 if not libs:3989 chart_css = ""3990 chart_description = ""3991 chart_figure = (3992 """3993 <figure class="highcharts-figure">3994 <div id="chartcontainer%s"></div>3995 <p class="highcharts-description">%s</p>3996 </figure>3997 """ % (self._chart_count, chart_description))3998 min_zero = ""3999 if zero:4000 min_zero = "min: 0,"4001 chart_init_1 = (4002 """4003 <script>4004 // Build the chart4005 Highcharts.chart('chartcontainer%s', {4006 credits: {4007 enabled: false4008 },4009 title: {4010 text: '%s'4011 },4012 subtitle: {4013 text: '%s'4014 },4015 xAxis: { },4016 yAxis: {4017 %s4018 title: {4019 text: '%s',4020 style: {4021 fontSize: '14px'4022 }4023 },4024 labels: {4025 useHTML: true,4026 style: {4027 fontSize: '14px'4028 }4029 }4030 },4031 chart: {4032 renderTo: 'statusChart',4033 plotBackgroundColor: null,4034 plotBorderWidth: null,4035 plotShadow: false,4036 type: '%s'4037 },4038 """ % (self._chart_count, title, subtitle, min_zero, unit, style))4039 # "{series.name}:"4040 point_format = (r'<b>{point.y}</b><br />'4041 r'<b>{point.percentage:.1f}%</b>')4042 if style != "pie":4043 point_format = (r'<b>{point.y}</b>')4044 chart_init_2 = (4045 """4046 tooltip: {4047 enabled: true,4048 useHTML: true,4049 style: {4050 padding: '6px',4051 fontSize: '14px'4052 },4053 pointFormat: '%s'4054 },4055 """ % point_format)4056 chart_init_3 = (4057 r"""4058 accessibility: {4059 point: {4060 valueSuffix: '%'4061 }4062 },4063 plotOptions: {4064 pie: {4065 size: "95%",4066 allowPointSelect: true,4067 animation: false,4068 cursor: 'pointer',4069 dataLabels: {4070 // enabled: false,4071 // format: '{point.name}: {point.y:.0f}',4072 formatter: function() {4073 if (this.y > 0) {4074 return this.point.name + ': ' + this.point.y4075 }4076 }4077 },4078 states: {4079 hover: {4080 enabled: true4081 }4082 },4083 showInLegend: true4084 }4085 },4086 """)4087 if style != "pie":4088 chart_init_3 = (4089 """4090 allowPointSelect: true,4091 cursor: 'pointer',4092 legend: {4093 layout: 'vertical',4094 align: 'right',4095 verticalAlign: 'middle'4096 },4097 states: {4098 hover: {4099 enabled: true4100 }4101 },4102 plotOptions: {4103 series: {4104 showInLegend: true,4105 animation: false,4106 dataLabels: {4107 enabled: true4108 },4109 shadow: false,4110 lineWidth: 3,4111 fillOpacity: 0.5,4112 marker: {4113 enabled: true4114 }4115 }4116 },4117 """)4118 chart_init = chart_init_1 + chart_init_2 + chart_init_34119 color_by_point = "true"4120 if style != "pie":4121 color_by_point = "false"4122 series = (4123 """4124 series: [{4125 name: '%s',4126 colorByPoint: %s,4127 data: [4128 """ % (data_name, color_by_point))4129 new_chart = chart_libs + chart_css + chart_figure + chart_init + series4130 self._chart_data[chart_name] = []4131 self._chart_label[chart_name] = []4132 self._chart_data[chart_name].append(new_chart)4133 self._chart_first_series[chart_name] = True4134 self._chart_series_count[chart_name] = 14135 def add_series_to_chart(self, data_name=None, chart_name=None):4136 """ Add a new data series to an existing chart.4137 This allows charts to have multiple data sets.4138 @Params4139 data_name - Set the series name. Useful for multi-series charts.4140 chart_name - If creating multiple charts,4141 use this to select which one.4142 """4143 if not chart_name:4144 chart_name = "default"4145 self._chart_series_count[chart_name] += 14146 if not data_name:4147 data_name = "Series %s" % self._chart_series_count[chart_name]4148 series = (4149 """4150 ]4151 },4152 {4153 name: '%s',4154 colorByPoint: false,4155 data: [4156 """ % data_name)4157 self._chart_data[chart_name].append(series)4158 self._chart_first_series[chart_name] = False4159 def add_data_point(self, label, value, color=None, chart_name=None):4160 """ Add a data point to a SeleniumBase-generated chart.4161 @Params4162 label - The label name for the data point.4163 value - The numeric value of the data point.4164 color - The HTML color of the data point.4165 Can be an RGB color. Eg: "#55ACDC".4166 Can also be a named color. Eg: "Teal".4167 chart_name - If creating multiple charts,4168 use this to select which one.4169 """4170 if not chart_name:4171 chart_name = "default"4172 if chart_name not in self._chart_data:4173 # Create a chart if it doesn't already exist4174 self.create_pie_chart(chart_name=chart_name)4175 if not value:4176 value = 04177 if not type(value) is int and not type(value) is float:4178 raise Exception('Expecting a numeric value for "value"!')4179 if not color:4180 color = ""4181 label = label.replace("'", "\\'")4182 color = color.replace("'", "\\'")4183 data_point = (4184 """4185 {4186 name: '%s',4187 y: %s,4188 color: '%s'4189 },4190 """ % (label, value, color))4191 self._chart_data[chart_name].append(data_point)4192 if self._chart_first_series[chart_name]:4193 self._chart_label[chart_name].append(label)4194 def save_chart(self, chart_name=None, filename=None, folder=None):4195 """ Saves a SeleniumBase-generated chart to a file for later use.4196 @Params4197 chart_name - If creating multiple charts at the same time,4198 use this to select the one you wish to use.4199 filename - The name of the HTML file that you wish to4200 save the chart to. (filename must end in ".html")4201 folder - The name of the folder where you wish to4202 save the HTML file. (Default: "./saved_charts/")4203 """4204 if not chart_name:4205 chart_name = "default"4206 if not filename:4207 filename = "my_chart.html"4208 if chart_name not in self._chart_data:4209 raise Exception("Chart {%s} does not exist!" % chart_name)4210 if not filename.endswith('.html'):4211 raise Exception('Chart file must end in ".html"!')4212 the_html = '<meta charset="utf-8">\n'4213 the_html += '<meta http-equiv="Content-Type" '4214 the_html += 'content="text/html, charset=utf-8;">\n'4215 the_html += '<meta name="viewport" content="shrink-to-fit=no">\n'4216 for chart_data_point in self._chart_data[chart_name]:4217 the_html += chart_data_point4218 the_html += (4219 """4220 ]4221 }]4222 });4223 </script>4224 """)4225 axis = "xAxis: {\n"4226 axis += " labels: {\n"4227 axis += " useHTML: true,\n"4228 axis += " style: {\n"4229 axis += " fontSize: '14px',\n"4230 axis += " },\n"4231 axis += " },\n"4232 axis += " categories: ["4233 for label in self._chart_label[chart_name]:4234 axis += "'%s'," % label4235 axis += "], crosshair: false},"4236 the_html = the_html.replace("xAxis: { },", axis)4237 if not folder:4238 saved_charts_folder = constants.Charts.SAVED_FOLDER4239 else:4240 saved_charts_folder = folder4241 if saved_charts_folder.endswith("/"):4242 saved_charts_folder = saved_charts_folder[:-1]4243 if not os.path.exists(saved_charts_folder):4244 try:4245 os.makedirs(saved_charts_folder)4246 except Exception:4247 pass4248 file_path = saved_charts_folder + "/" + filename4249 out_file = codecs.open(file_path, "w+", encoding="utf-8")4250 out_file.writelines(the_html)4251 out_file.close()4252 print('\n>>> [%s] was saved!' % file_path)4253 return file_path4254 def display_chart(self, chart_name=None, filename=None, interval=0):4255 """ Displays a SeleniumBase-generated chart in the browser window.4256 @Params4257 chart_name - If creating multiple charts at the same time,4258 use this to select the one you wish to use.4259 filename - The name of the HTML file that you wish to4260 save the chart to. (filename must end in ".html")4261 interval - The delay time for auto-advancing charts. (in seconds)4262 If set to 0 (default), auto-advancing is disabled.4263 """4264 if self.headless:4265 interval = 1 # Race through chart if running in headless mode4266 if not chart_name:4267 chart_name = "default"4268 if not filename:4269 filename = "my_chart.html"4270 if not interval:4271 interval = 04272 if not type(interval) is int and not type(interval) is float:4273 raise Exception('Expecting a numeric value for "interval"!')4274 if interval < 0:4275 raise Exception('The "interval" cannot be a negative number!')4276 if chart_name not in self._chart_data:4277 raise Exception("Chart {%s} does not exist!" % chart_name)4278 if not filename.endswith('.html'):4279 raise Exception('Chart file must end in ".html"!')4280 file_path = self.save_chart(chart_name=chart_name, filename=filename)4281 self.open_html_file(file_path)4282 chart_folder = constants.Charts.SAVED_FOLDER4283 if interval == 0:4284 try:4285 print("\n*** Close the browser window to continue ***")4286 # Will also continue if manually navigating to a new page4287 while (len(self.driver.window_handles) > 0 and (4288 chart_folder in self.get_current_url())):4289 time.sleep(0.05)4290 except Exception:4291 pass4292 else:4293 try:4294 start_ms = time.time() * 1000.04295 stop_ms = start_ms + (interval * 1000.0)4296 for x in range(int(interval * 10)):4297 now_ms = time.time() * 1000.04298 if now_ms >= stop_ms:4299 break4300 if len(self.driver.window_handles) == 0:4301 break4302 if chart_folder not in self.get_current_url():4303 break4304 time.sleep(0.1)4305 except Exception:4306 pass4307 def extract_chart(self, chart_name=None):4308 """ Extracts the HTML from a SeleniumBase-generated chart.4309 @Params4310 chart_name - If creating multiple charts at the same time,4311 use this to select the one you wish to use.4312 """4313 if not chart_name:4314 chart_name = "default"4315 if chart_name not in self._chart_data:4316 raise Exception("Chart {%s} does not exist!" % chart_name)4317 the_html = ""4318 for chart_data_point in self._chart_data[chart_name]:4319 the_html += chart_data_point4320 the_html += (4321 """4322 ]4323 }]4324 });4325 </script>4326 """)4327 axis = "xAxis: {\n"4328 axis += " labels: {\n"4329 axis += " useHTML: true,\n"4330 axis += " style: {\n"4331 axis += " fontSize: '14px',\n"4332 axis += " },\n"4333 axis += " },\n"4334 axis += " categories: ["4335 for label in self._chart_label[chart_name]:4336 axis += "'%s'," % label4337 axis += "], crosshair: false},"4338 the_html = the_html.replace("xAxis: { },", axis)4339 return the_html4340 ############4341 def create_tour(self, name=None, theme=None):4342 """ Creates a tour for a website. By default, the Shepherd JavaScript4343 Library is used with the Shepherd "Light" / "Arrows" theme.4344 @Params4345 name - If creating multiple tours at the same time,4346 use this to select the tour you wish to add steps to.4347 theme - Sets the default theme for the tour.4348 Choose from "light"/"arrows", "dark", "default", "square",4349 and "square-dark". ("arrows" is used if None is selected.)4350 Alternatively, you may use a different JavaScript Library4351 as the theme. Those include "IntroJS", "DriverJS",4352 "Hopscotch", and "Bootstrap".4353 """4354 if not name:4355 name = "default"4356 if theme:4357 if theme.lower() == "bootstrap":4358 self.create_bootstrap_tour(name)4359 elif theme.lower() == "hopscotch":4360 self.create_hopscotch_tour(name)4361 elif theme.lower() == "intro":4362 self.create_introjs_tour(name)4363 elif theme.lower() == "introjs":4364 self.create_introjs_tour(name)4365 elif theme.lower() == "driver":4366 self.create_driverjs_tour(name)4367 elif theme.lower() == "driverjs":4368 self.create_driverjs_tour(name)4369 elif theme.lower() == "shepherd":4370 self.create_shepherd_tour(name, theme="light")4371 elif theme.lower() == "light":4372 self.create_shepherd_tour(name, theme="light")4373 elif theme.lower() == "dark":4374 self.create_shepherd_tour(name, theme="dark")4375 elif theme.lower() == "arrows":4376 self.create_shepherd_tour(name, theme="light")4377 elif theme.lower() == "square":4378 self.create_shepherd_tour(name, theme="square")4379 elif theme.lower() == "square-dark":4380 self.create_shepherd_tour(name, theme="square-dark")4381 elif theme.lower() == "default":4382 self.create_shepherd_tour(name, theme="default")4383 else:4384 self.create_shepherd_tour(name, theme)4385 else:4386 self.create_shepherd_tour(name, theme="light")4387 def create_shepherd_tour(self, name=None, theme=None):4388 """ Creates a Shepherd JS website tour.4389 @Params4390 name - If creating multiple tours at the same time,4391 use this to select the tour you wish to add steps to.4392 theme - Sets the default theme for the tour.4393 Choose from "light"/"arrows", "dark", "default", "square",4394 and "square-dark". ("light" is used if None is selected.)4395 """4396 shepherd_theme = "shepherd-theme-arrows"4397 if theme:4398 if theme.lower() == "default":4399 shepherd_theme = "shepherd-theme-default"4400 elif theme.lower() == "dark":4401 shepherd_theme = "shepherd-theme-dark"4402 elif theme.lower() == "light":4403 shepherd_theme = "shepherd-theme-arrows"4404 elif theme.lower() == "arrows":4405 shepherd_theme = "shepherd-theme-arrows"4406 elif theme.lower() == "square":4407 shepherd_theme = "shepherd-theme-square"4408 elif theme.lower() == "square-dark":4409 shepherd_theme = "shepherd-theme-square-dark"4410 if not name:4411 name = "default"4412 new_tour = (4413 """4414 // Shepherd Tour4415 var tour = new Shepherd.Tour({4416 defaults: {4417 classes: '%s',4418 scrollTo: true4419 }4420 });4421 var allButtons = {4422 skip: {4423 text: "Skip",4424 action: tour.cancel,4425 classes: 'shepherd-button-secondary tour-button-left'4426 },4427 back: {4428 text: "Back",4429 action: tour.back,4430 classes: 'shepherd-button-secondary'4431 },4432 next: {4433 text: "Next",4434 action: tour.next,4435 classes: 'shepherd-button-primary tour-button-right'4436 },4437 };4438 var firstStepButtons = [allButtons.skip, allButtons.next];4439 var midTourButtons = [allButtons.back, allButtons.next];4440 """ % shepherd_theme)4441 self._tour_steps[name] = []4442 self._tour_steps[name].append(new_tour)4443 def create_bootstrap_tour(self, name=None):4444 """ Creates a Bootstrap tour for a website.4445 @Params4446 name - If creating multiple tours at the same time,4447 use this to select the tour you wish to add steps to.4448 """4449 if not name:4450 name = "default"4451 new_tour = (4452 """4453 // Bootstrap Tour4454 var tour = new Tour({4455 });4456 tour.addSteps([4457 """)4458 self._tour_steps[name] = []4459 self._tour_steps[name].append(new_tour)4460 def create_driverjs_tour(self, name=None):4461 """ Creates a DriverJS tour for a website.4462 @Params4463 name - If creating multiple tours at the same time,4464 use this to select the tour you wish to add steps to.4465 """4466 if not name:4467 name = "default"4468 new_tour = (4469 """4470 // DriverJS Tour4471 var tour = new Driver({4472 opacity: 0.24, // Background opacity (0: no popover / overlay)4473 padding: 6, // Distance of element from around the edges4474 allowClose: false, // Whether clicking on overlay should close4475 overlayClickNext: false, // Move to next step on overlay click4476 doneBtnText: 'Done', // Text that appears on the Done button4477 closeBtnText: 'Close', // Text appearing on the Close button4478 nextBtnText: 'Next', // Text that appears on the Next button4479 prevBtnText: 'Previous', // Text appearing on Previous button4480 showButtons: true, // This shows control buttons in the footer4481 keyboardControl: true, // (escape to close, arrow keys to move)4482 animate: true, // Animate while changing highlighted element4483 });4484 tour.defineSteps([4485 """)4486 self._tour_steps[name] = []4487 self._tour_steps[name].append(new_tour)4488 def create_hopscotch_tour(self, name=None):4489 """ Creates a Hopscotch tour for a website.4490 @Params4491 name - If creating multiple tours at the same time,4492 use this to select the tour you wish to add steps to.4493 """4494 if not name:4495 name = "default"4496 new_tour = (4497 """4498 // Hopscotch Tour4499 var tour = {4500 id: "hopscotch_tour",4501 steps: [4502 """)4503 self._tour_steps[name] = []4504 self._tour_steps[name].append(new_tour)4505 def create_introjs_tour(self, name=None):4506 """ Creates an IntroJS tour for a website.4507 @Params4508 name - If creating multiple tours at the same time,4509 use this to select the tour you wish to add steps to.4510 """4511 if not name:4512 name = "default"4513 new_tour = (4514 """4515 // IntroJS Tour4516 function startIntro(){4517 var intro = introJs();4518 intro.setOptions({4519 steps: [4520 """)4521 self._tour_steps[name] = []4522 self._tour_steps[name].append(new_tour)4523 def add_tour_step(self, message, selector=None, name=None,4524 title=None, theme=None, alignment=None, duration=None):4525 """ Allows the user to add tour steps for a website.4526 @Params4527 message - The message to display.4528 selector - The CSS Selector of the Element to attach to.4529 name - If creating multiple tours at the same time,4530 use this to select the tour you wish to add steps to.4531 title - Additional header text that appears above the message.4532 theme - (NON-Bootstrap Tours ONLY) The styling of the tour step.4533 Choose from "light"/"arrows", "dark", "default", "square",4534 and "square-dark". ("arrows" is used if None is selected.)4535 alignment - Choose from "top", "bottom", "left", and "right".4536 ("top" is default, except for Hopscotch and DriverJS).4537 duration - (Bootstrap Tours ONLY) The amount of time, in seconds,4538 before automatically advancing to the next tour step.4539 """4540 if not selector:4541 selector = "html"4542 if page_utils.is_name_selector(selector):4543 name = page_utils.get_name_from_selector(selector)4544 selector = '[name="%s"]' % name4545 if page_utils.is_xpath_selector(selector):4546 selector = self.convert_to_css_selector(selector, By.XPATH)4547 selector = self.__escape_quotes_if_needed(selector)4548 if not name:4549 name = "default"4550 if name not in self._tour_steps:4551 # By default, will create an IntroJS tour if no tours exist4552 self.create_tour(name=name, theme="introjs")4553 if not title:4554 title = ""4555 title = self.__escape_quotes_if_needed(title)4556 if message:4557 message = self.__escape_quotes_if_needed(message)4558 else:4559 message = ""4560 if not alignment or (4561 alignment not in ["top", "bottom", "left", "right"]):4562 t_name = self._tour_steps[name][0]4563 if "Hopscotch" not in t_name and "DriverJS" not in t_name:4564 alignment = "top"4565 else:4566 alignment = "bottom"4567 if "Bootstrap" in self._tour_steps[name][0]:4568 self.__add_bootstrap_tour_step(4569 message, selector=selector, name=name, title=title,4570 alignment=alignment, duration=duration)4571 elif "DriverJS" in self._tour_steps[name][0]:4572 self.__add_driverjs_tour_step(4573 message, selector=selector, name=name, title=title,4574 alignment=alignment)4575 elif "Hopscotch" in self._tour_steps[name][0]:4576 self.__add_hopscotch_tour_step(4577 message, selector=selector, name=name, title=title,4578 alignment=alignment)4579 elif "IntroJS" in self._tour_steps[name][0]:4580 self.__add_introjs_tour_step(4581 message, selector=selector, name=name, title=title,4582 alignment=alignment)4583 else:4584 self.__add_shepherd_tour_step(4585 message, selector=selector, name=name, title=title,4586 theme=theme, alignment=alignment)4587 def __add_shepherd_tour_step(self, message, selector=None, name=None,4588 title=None, theme=None, alignment=None):4589 """ Allows the user to add tour steps for a website.4590 @Params4591 message - The message to display.4592 selector - The CSS Selector of the Element to attach to.4593 name - If creating multiple tours at the same time,4594 use this to select the tour you wish to add steps to.4595 title - Additional header text that appears above the message.4596 theme - (NON-Bootstrap Tours ONLY) The styling of the tour step.4597 Choose from "light"/"arrows", "dark", "default", "square",4598 and "square-dark". ("arrows" is used if None is selected.)4599 alignment - Choose from "top", "bottom", "left", and "right".4600 ("top" is the default alignment).4601 """4602 if theme == "default":4603 shepherd_theme = "shepherd-theme-default"4604 elif theme == "dark":4605 shepherd_theme = "shepherd-theme-dark"4606 elif theme == "light":4607 shepherd_theme = "shepherd-theme-arrows"4608 elif theme == "arrows":4609 shepherd_theme = "shepherd-theme-arrows"4610 elif theme == "square":4611 shepherd_theme = "shepherd-theme-square"4612 elif theme == "square-dark":4613 shepherd_theme = "shepherd-theme-square-dark"4614 else:4615 shepherd_base_theme = re.search(4616 r"[\S\s]+classes: '([\S\s]+)',[\S\s]+",4617 self._tour_steps[name][0]).group(1)4618 shepherd_theme = shepherd_base_theme4619 shepherd_classes = shepherd_theme4620 if selector == "html":4621 shepherd_classes += " shepherd-orphan"4622 buttons = "firstStepButtons"4623 if len(self._tour_steps[name]) > 1:4624 buttons = "midTourButtons"4625 step = ("""4626 tour.addStep('%s', {4627 title: '%s',4628 classes: '%s',4629 text: '%s',4630 attachTo: {element: '%s', on: '%s'},4631 buttons: %s,4632 advanceOn: '.docs-link click'4633 });""" % (4634 name, title, shepherd_classes, message, selector, alignment,4635 buttons))4636 self._tour_steps[name].append(step)4637 def __add_bootstrap_tour_step(self, message, selector=None, name=None,4638 title=None, alignment=None, duration=None):4639 """ Allows the user to add tour steps for a website.4640 @Params4641 message - The message to display.4642 selector - The CSS Selector of the Element to attach to.4643 name - If creating multiple tours at the same time,4644 use this to select the tour you wish to add steps to.4645 title - Additional header text that appears above the message.4646 alignment - Choose from "top", "bottom", "left", and "right".4647 ("top" is the default alignment).4648 duration - (Bootstrap Tours ONLY) The amount of time, in seconds,4649 before automatically advancing to the next tour step.4650 """4651 if selector != "html":4652 selector = self.__make_css_match_first_element_only(selector)4653 element_row = "element: '%s'," % selector4654 else:4655 element_row = ""4656 if not duration:4657 duration = "0"4658 else:4659 duration = str(float(duration) * 1000.0)4660 step = ("""{4661 %s4662 title: '%s',4663 content: '%s',4664 orphan: true,4665 placement: 'auto %s',4666 smartPlacement: true,4667 duration: %s,4668 },""" % (element_row, title, message, alignment, duration))4669 self._tour_steps[name].append(step)4670 def __add_driverjs_tour_step(self, message, selector=None, name=None,4671 title=None, alignment=None):4672 """ Allows the user to add tour steps for a website.4673 @Params4674 message - The message to display.4675 selector - The CSS Selector of the Element to attach to.4676 name - If creating multiple tours at the same time,4677 use this to select the tour you wish to add steps to.4678 title - Additional header text that appears above the message.4679 alignment - Choose from "top", "bottom", "left", and "right".4680 ("top" is the default alignment).4681 """4682 message = (4683 '<font size=\"3\" color=\"#33477B\"><b>' + message + '</b></font>')4684 title_row = ""4685 if not title:4686 title_row = "title: '%s'," % message4687 message = ""4688 else:4689 title_row = "title: '%s'," % title4690 align_row = "position: '%s'," % alignment4691 ani_row = "animate: true,"4692 if not selector or selector == "html" or selector == "body":4693 selector = "body"4694 ani_row = "animate: false,"4695 align_row = "position: '%s'," % "mid-center"4696 element_row = "element: '%s'," % selector4697 desc_row = "description: '%s'," % message4698 step = ("""{4699 %s4700 %s4701 popover: {4702 className: 'popover-class',4703 %s4704 %s4705 %s4706 }4707 },""" % (element_row, ani_row, title_row, desc_row, align_row))4708 self._tour_steps[name].append(step)4709 def __add_hopscotch_tour_step(self, message, selector=None, name=None,4710 title=None, alignment=None):4711 """ Allows the user to add tour steps for a website.4712 @Params4713 message - The message to display.4714 selector - The CSS Selector of the Element to attach to.4715 name - If creating multiple tours at the same time,4716 use this to select the tour you wish to add steps to.4717 title - Additional header text that appears above the message.4718 alignment - Choose from "top", "bottom", "left", and "right".4719 ("bottom" is the default alignment).4720 """4721 arrow_offset_row = None4722 if not selector or selector == "html":4723 selector = "head"4724 alignment = "bottom"4725 arrow_offset_row = "arrowOffset: '200',"4726 else:4727 arrow_offset_row = ""4728 step = ("""{4729 target: '%s',4730 title: '%s',4731 content: '%s',4732 %s4733 showPrevButton: 'true',4734 scrollDuration: '550',4735 placement: '%s'},4736 """ % (selector, title, message, arrow_offset_row, alignment))4737 self._tour_steps[name].append(step)4738 def __add_introjs_tour_step(self, message, selector=None, name=None,4739 title=None, alignment=None):4740 """ Allows the user to add tour steps for a website.4741 @Params4742 message - The message to display.4743 selector - The CSS Selector of the Element to attach to.4744 name - If creating multiple tours at the same time,4745 use this to select the tour you wish to add steps to.4746 title - Additional header text that appears above the message.4747 alignment - Choose from "top", "bottom", "left", and "right".4748 ("top" is the default alignment).4749 """4750 if selector != "html":4751 element_row = "element: '%s'," % selector4752 else:4753 element_row = ""4754 if title:4755 message = "<center><b>" + title + "</b></center><hr>" + message4756 message = '<font size=\"3\" color=\"#33477B\">' + message + '</font>'4757 step = ("""{%s4758 intro: '%s',4759 position: '%s'},4760 """ % (element_row, message, alignment))4761 self._tour_steps[name].append(step)4762 def play_tour(self, name=None, interval=0):4763 """ Plays a tour on the current website.4764 @Params4765 name - If creating multiple tours at the same time,4766 use this to select the tour you wish to add steps to.4767 interval - The delay time between autoplaying tour steps. (Seconds)4768 If set to 0 (default), the tour is fully manual control.4769 """4770 if self.headless:4771 return # Tours should not run in headless mode.4772 if not name:4773 name = "default"4774 if name not in self._tour_steps:4775 raise Exception("Tour {%s} does not exist!" % name)4776 if "Bootstrap" in self._tour_steps[name][0]:4777 tour_helper.play_bootstrap_tour(4778 self.driver, self._tour_steps, self.browser,4779 self.message_duration, name=name, interval=interval)4780 elif "DriverJS" in self._tour_steps[name][0]:4781 tour_helper.play_driverjs_tour(4782 self.driver, self._tour_steps, self.browser,4783 self.message_duration, name=name, interval=interval)4784 elif "Hopscotch" in self._tour_steps[name][0]:4785 tour_helper.play_hopscotch_tour(4786 self.driver, self._tour_steps, self.browser,4787 self.message_duration, name=name, interval=interval)4788 elif "IntroJS" in self._tour_steps[name][0]:4789 tour_helper.play_introjs_tour(4790 self.driver, self._tour_steps, self.browser,4791 self.message_duration, name=name, interval=interval)4792 else:4793 # "Shepherd"4794 tour_helper.play_shepherd_tour(4795 self.driver, self._tour_steps,4796 self.message_duration, name=name, interval=interval)4797 def export_tour(self, name=None, filename="my_tour.js", url=None):4798 """ Exports a tour as a JS file.4799 You can call self.export_tour() anywhere where you would4800 normally use self.play_tour() to play a website tour.4801 It will include necessary resources as well, such as jQuery.4802 You'll be able to copy the tour directly into the Console of4803 any web browser to play the tour outside of SeleniumBase runs.4804 @Params4805 name - If creating multiple tours at the same time,4806 use this to select the tour you wish to add steps to.4807 filename - The name of the JavaScript file that you wish to4808 save the tour to.4809 url - The URL where the tour starts. If not specified, the URL4810 of the current page will be used. """4811 if not url:4812 url = self.get_current_url()4813 tour_helper.export_tour(4814 self._tour_steps, name=name, filename=filename, url=url)4815 def activate_jquery_confirm(self):4816 """ See https://craftpip.github.io/jquery-confirm/ for usage. """4817 self.__check_scope()4818 js_utils.activate_jquery_confirm(self.driver)4819 self.wait_for_ready_state_complete()4820 def activate_messenger(self):4821 self.__check_scope()4822 js_utils.activate_messenger(self.driver)4823 self.wait_for_ready_state_complete()4824 def set_messenger_theme(self, theme="default", location="default",4825 max_messages="default"):4826 """ Sets a theme for posting messages.4827 Themes: ["flat", "future", "block", "air", "ice"]4828 Locations: ["top_left", "top_center", "top_right",4829 "bottom_left", "bottom_center", "bottom_right"]4830 max_messages is the limit of concurrent messages to display. """4831 self.__check_scope()4832 if not theme:4833 theme = "default" # "flat"4834 if not location:4835 location = "default" # "bottom_right"4836 if not max_messages:4837 max_messages = "default" # "8"4838 else:4839 max_messages = str(max_messages) # Value must be in string format4840 js_utils.set_messenger_theme(4841 self.driver, theme=theme,4842 location=location, max_messages=max_messages)4843 def post_message(self, message, duration=None, pause=True, style="info"):4844 """ Post a message on the screen with Messenger.4845 Arguments:4846 message: The message to display.4847 duration: The time until the message vanishes. (Default: 2.55s)4848 pause: If True, the program waits until the message completes.4849 style: "info", "success", or "error".4850 You can also post messages by using =>4851 self.execute_script('Messenger().post("My Message")')4852 """4853 self.__check_scope()4854 if style not in ["info", "success", "error"]:4855 style = "info"4856 if not duration:4857 if not self.message_duration:4858 duration = settings.DEFAULT_MESSAGE_DURATION4859 else:4860 duration = self.message_duration4861 try:4862 js_utils.post_message(self.driver, message, duration, style=style)4863 except Exception:4864 print(" * %s message: %s" % (style.upper(), message))4865 if pause:4866 duration = float(duration) + 0.154867 time.sleep(float(duration))4868 def post_message_and_highlight(4869 self, message, selector, by=By.CSS_SELECTOR):4870 """ Post a message on the screen and highlight an element.4871 Arguments:4872 message: The message to display.4873 selector: The selector of the Element to highlight.4874 by: The type of selector to search by. (Default: CSS Selector)4875 """4876 self.__check_scope()4877 self.__highlight_with_assert_success(message, selector, by=by)4878 def post_success_message(self, message, duration=None, pause=True):4879 """ Post a success message on the screen with Messenger.4880 Arguments:4881 message: The success message to display.4882 duration: The time until the message vanishes. (Default: 2.55s)4883 pause: If True, the program waits until the message completes.4884 """4885 self.__check_scope()4886 if not duration:4887 if not self.message_duration:4888 duration = settings.DEFAULT_MESSAGE_DURATION4889 else:4890 duration = self.message_duration4891 try:4892 js_utils.post_message(4893 self.driver, message, duration, style="success")4894 except Exception:4895 print(" * SUCCESS message: %s" % message)4896 if pause:4897 duration = float(duration) + 0.154898 time.sleep(float(duration))4899 def post_error_message(self, message, duration=None, pause=True):4900 """ Post an error message on the screen with Messenger.4901 Arguments:4902 message: The error message to display.4903 duration: The time until the message vanishes. (Default: 2.55s)4904 pause: If True, the program waits until the message completes.4905 """4906 self.__check_scope()4907 if not duration:4908 if not self.message_duration:4909 duration = settings.DEFAULT_MESSAGE_DURATION4910 else:4911 duration = self.message_duration4912 try:4913 js_utils.post_message(4914 self.driver, message, duration, style="error")4915 except Exception:4916 print(" * ERROR message: %s" % message)4917 if pause:4918 duration = float(duration) + 0.154919 time.sleep(float(duration))4920 ############4921 def generate_referral(self, start_page, destination_page):4922 """ This method opens the start_page, creates a referral link there,4923 and clicks on that link, which goes to the destination_page.4924 (This generates real traffic for testing analytics software.) """4925 self.__check_scope()4926 if not page_utils.is_valid_url(destination_page):4927 raise Exception(4928 "Exception: destination_page {%s} is not a valid URL!"4929 % destination_page)4930 if start_page:4931 if not page_utils.is_valid_url(start_page):4932 raise Exception(4933 "Exception: start_page {%s} is not a valid URL! "4934 "(Use an empty string or None to start from current page.)"4935 % start_page)4936 self.open(start_page)4937 time.sleep(0.08)4938 self.wait_for_ready_state_complete()4939 referral_link = ('''<body>'''4940 '''<a class='analytics referral test' href='%s' '''4941 '''style='font-family: Arial,sans-serif; '''4942 '''font-size: 30px; color: #18a2cd'>'''4943 '''Magic Link Button</a></body>''' % destination_page)4944 self.execute_script(4945 '''document.body.outerHTML = \"%s\"''' % referral_link)4946 self.click(4947 "a.analytics.referral.test", timeout=2) # Clicks generated button4948 time.sleep(0.15)4949 try:4950 self.click("html")4951 time.sleep(0.08)4952 except Exception:4953 pass4954 def generate_traffic(self, start_page, destination_page, loops=1):4955 """ Similar to generate_referral(), but can do multiple loops. """4956 self.__check_scope()4957 for loop in range(loops):4958 self.generate_referral(start_page, destination_page)4959 time.sleep(0.05)4960 def generate_referral_chain(self, pages):4961 """ Use this method to chain the action of creating button links on4962 one website page that will take you to the next page.4963 (When you want to create a referral to a website for traffic4964 generation without increasing the bounce rate, you'll want to visit4965 at least one additional page on that site with a button click.) """4966 self.__check_scope()4967 if not type(pages) is tuple and not type(pages) is list:4968 raise Exception(4969 "Exception: Expecting a list of website pages for chaining!")4970 if len(pages) < 2:4971 raise Exception(4972 "Exception: At least two website pages required for chaining!")4973 for page in pages:4974 # Find out if any of the web pages are invalid before continuing4975 if not page_utils.is_valid_url(page):4976 raise Exception(4977 "Exception: Website page {%s} is not a valid URL!" % page)4978 for page in pages:4979 self.generate_referral(None, page)4980 def generate_traffic_chain(self, pages, loops=1):4981 """ Similar to generate_referral_chain(), but for multiple loops. """4982 self.__check_scope()4983 for loop in range(loops):4984 self.generate_referral_chain(pages)4985 time.sleep(0.05)4986 ############4987 def wait_for_element_present(self, selector, by=By.CSS_SELECTOR,4988 timeout=None):4989 """ Waits for an element to appear in the HTML of a page.4990 The element does not need be visible (it may be hidden). """4991 self.__check_scope()4992 if not timeout:4993 timeout = settings.LARGE_TIMEOUT4994 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4995 timeout = self.__get_new_timeout(timeout)4996 selector, by = self.__recalculate_selector(selector, by)4997 return page_actions.wait_for_element_present(4998 self.driver, selector, by, timeout)4999 def wait_for_element(self, selector, by=By.CSS_SELECTOR, timeout=None):5000 """ Waits for an element to appear in the HTML of a page.5001 The element must be visible (it cannot be hidden). """5002 self.__check_scope()5003 if not timeout:5004 timeout = settings.LARGE_TIMEOUT5005 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5006 timeout = self.__get_new_timeout(timeout)5007 selector, by = self.__recalculate_selector(selector, by)5008 return page_actions.wait_for_element_visible(5009 self.driver, selector, by, timeout)5010 def get_element(self, selector, by=By.CSS_SELECTOR, timeout=None):5011 """ Same as wait_for_element_present() - returns the element.5012 The element does not need be visible (it may be hidden). """5013 self.__check_scope()5014 if not timeout:5015 timeout = settings.SMALL_TIMEOUT5016 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5017 timeout = self.__get_new_timeout(timeout)5018 selector, by = self.__recalculate_selector(selector, by)5019 return self.wait_for_element_present(selector, by=by, timeout=timeout)5020 def assert_element_present(self, selector, by=By.CSS_SELECTOR,5021 timeout=None):5022 """ Similar to wait_for_element_present(), but returns nothing.5023 Waits for an element to appear in the HTML of a page.5024 The element does not need be visible (it may be hidden).5025 Returns True if successful. Default timeout = SMALL_TIMEOUT. """5026 self.__check_scope()5027 if not timeout:5028 timeout = settings.SMALL_TIMEOUT5029 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5030 timeout = self.__get_new_timeout(timeout)5031 self.wait_for_element_present(selector, by=by, timeout=timeout)5032 return True5033 def find_element(self, selector, by=By.CSS_SELECTOR, timeout=None):5034 """ Same as wait_for_element_visible() - returns the element """5035 self.__check_scope()5036 if not timeout:5037 timeout = settings.LARGE_TIMEOUT5038 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5039 timeout = self.__get_new_timeout(timeout)5040 return self.wait_for_element_visible(selector, by=by, timeout=timeout)5041 def assert_element(self, selector, by=By.CSS_SELECTOR, timeout=None):5042 """ Similar to wait_for_element_visible(), but returns nothing.5043 As above, will raise an exception if nothing can be found.5044 Returns True if successful. Default timeout = SMALL_TIMEOUT. """5045 self.__check_scope()5046 if not timeout:5047 timeout = settings.SMALL_TIMEOUT5048 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5049 timeout = self.__get_new_timeout(timeout)5050 self.wait_for_element_visible(selector, by=by, timeout=timeout)5051 if self.demo_mode:5052 selector, by = self.__recalculate_selector(selector, by)5053 a_t = "ASSERT"5054 if self._language != "English":5055 from seleniumbase.fixtures.words import SD5056 a_t = SD.translate_assert(self._language)5057 messenger_post = "%s %s: %s" % (a_t, by.upper(), selector)5058 self.__highlight_with_assert_success(messenger_post, selector, by)5059 return True5060 def assert_element_visible(self, selector, by=By.CSS_SELECTOR,5061 timeout=None):5062 """ Same as self.assert_element()5063 As above, will raise an exception if nothing can be found. """5064 self.__check_scope()5065 if not timeout:5066 timeout = settings.SMALL_TIMEOUT5067 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5068 timeout = self.__get_new_timeout(timeout)5069 self.assert_element(selector, by=by, timeout=timeout)5070 return True5071 ############5072 def wait_for_text_visible(self, text, selector="html", by=By.CSS_SELECTOR,5073 timeout=None):5074 self.__check_scope()5075 if not timeout:5076 timeout = settings.LARGE_TIMEOUT5077 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5078 timeout = self.__get_new_timeout(timeout)5079 selector, by = self.__recalculate_selector(selector, by)5080 return page_actions.wait_for_text_visible(5081 self.driver, text, selector, by, timeout)5082 def wait_for_exact_text_visible(self, text, selector="html",5083 by=By.CSS_SELECTOR,5084 timeout=None):5085 self.__check_scope()5086 if not timeout:5087 timeout = settings.LARGE_TIMEOUT5088 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5089 timeout = self.__get_new_timeout(timeout)5090 selector, by = self.__recalculate_selector(selector, by)5091 return page_actions.wait_for_exact_text_visible(5092 self.driver, text, selector, by, timeout)5093 def wait_for_text(self, text, selector="html", by=By.CSS_SELECTOR,5094 timeout=None):5095 """ The shorter version of wait_for_text_visible() """5096 self.__check_scope()5097 if not timeout:5098 timeout = settings.LARGE_TIMEOUT5099 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5100 timeout = self.__get_new_timeout(timeout)5101 return self.wait_for_text_visible(5102 text, selector, by=by, timeout=timeout)5103 def find_text(self, text, selector="html", by=By.CSS_SELECTOR,5104 timeout=None):5105 """ Same as wait_for_text_visible() - returns the element """5106 self.__check_scope()5107 if not timeout:5108 timeout = settings.LARGE_TIMEOUT5109 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5110 timeout = self.__get_new_timeout(timeout)5111 return self.wait_for_text_visible(5112 text, selector, by=by, timeout=timeout)5113 def assert_text_visible(self, text, selector="html", by=By.CSS_SELECTOR,5114 timeout=None):5115 """ Same as assert_text() """5116 self.__check_scope()5117 if not timeout:5118 timeout = settings.SMALL_TIMEOUT5119 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5120 timeout = self.__get_new_timeout(timeout)5121 return self.assert_text(text, selector, by=by, timeout=timeout)5122 def assert_text(self, text, selector="html", by=By.CSS_SELECTOR,5123 timeout=None):5124 """ Similar to wait_for_text_visible()5125 Raises an exception if the element or the text is not found.5126 Returns True if successful. Default timeout = SMALL_TIMEOUT. """5127 self.__check_scope()5128 if not timeout:5129 timeout = settings.SMALL_TIMEOUT5130 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5131 timeout = self.__get_new_timeout(timeout)5132 self.wait_for_text_visible(text, selector, by=by, timeout=timeout)5133 if self.demo_mode:5134 selector, by = self.__recalculate_selector(selector, by)5135 a_t = "ASSERT TEXT"5136 i_n = "in"5137 if self._language != "English":5138 from seleniumbase.fixtures.words import SD5139 a_t = SD.translate_assert_text(self._language)5140 i_n = SD.translate_in(self._language)5141 messenger_post = ("%s: {%s} %s %s: %s"5142 % (a_t, text, i_n, by.upper(), selector))5143 self.__highlight_with_assert_success(messenger_post, selector, by)5144 return True5145 def assert_exact_text(self, text, selector="html", by=By.CSS_SELECTOR,5146 timeout=None):5147 """ Similar to assert_text(), but the text must be exact, rather than5148 exist as a subset of the full text.5149 (Extra whitespace at the beginning or the end doesn't count.)5150 Raises an exception if the element or the text is not found.5151 Returns True if successful. Default timeout = SMALL_TIMEOUT. """5152 self.__check_scope()5153 if not timeout:5154 timeout = settings.SMALL_TIMEOUT5155 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5156 timeout = self.__get_new_timeout(timeout)5157 self.wait_for_exact_text_visible(5158 text, selector, by=by, timeout=timeout)5159 if self.demo_mode:5160 selector, by = self.__recalculate_selector(selector, by)5161 a_t = "ASSERT EXACT TEXT"5162 i_n = "in"5163 if self._language != "English":5164 from seleniumbase.fixtures.words import SD5165 a_t = SD.translate_assert_exact_text(self._language)5166 i_n = SD.translate_in(self._language)5167 messenger_post = ("%s: {%s} %s %s: %s"5168 % (a_t, text, i_n, by.upper(), selector))5169 self.__highlight_with_assert_success(messenger_post, selector, by)5170 return True5171 ############5172 def wait_for_link_text_present(self, link_text, timeout=None):5173 self.__check_scope()5174 if not timeout:5175 timeout = settings.SMALL_TIMEOUT5176 start_ms = time.time() * 1000.05177 stop_ms = start_ms + (timeout * 1000.0)5178 for x in range(int(timeout * 5)):5179 shared_utils.check_if_time_limit_exceeded()5180 try:5181 if not self.is_link_text_present(link_text):5182 raise Exception(5183 "Link text {%s} was not found!" % link_text)5184 return5185 except Exception:5186 now_ms = time.time() * 1000.05187 if now_ms >= stop_ms:5188 break5189 time.sleep(0.2)5190 message = (5191 "Link text {%s} was not present after %s seconds!"5192 "" % (link_text, timeout))5193 page_actions.timeout_exception("NoSuchElementException", message)5194 def wait_for_partial_link_text_present(self, link_text, timeout=None):5195 self.__check_scope()5196 if not timeout:5197 timeout = settings.SMALL_TIMEOUT5198 start_ms = time.time() * 1000.05199 stop_ms = start_ms + (timeout * 1000.0)5200 for x in range(int(timeout * 5)):5201 shared_utils.check_if_time_limit_exceeded()5202 try:5203 if not self.is_partial_link_text_present(link_text):5204 raise Exception(5205 "Partial Link text {%s} was not found!" % link_text)5206 return5207 except Exception:5208 now_ms = time.time() * 1000.05209 if now_ms >= stop_ms:5210 break5211 time.sleep(0.2)5212 message = (5213 "Partial Link text {%s} was not present after %s seconds!"5214 "" % (link_text, timeout))5215 page_actions.timeout_exception("NoSuchElementException", message)5216 def wait_for_link_text_visible(self, link_text, timeout=None):5217 self.__check_scope()5218 if not timeout:5219 timeout = settings.LARGE_TIMEOUT5220 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5221 timeout = self.__get_new_timeout(timeout)5222 return self.wait_for_element_visible(5223 link_text, by=By.LINK_TEXT, timeout=timeout)5224 def wait_for_link_text(self, link_text, timeout=None):5225 """ The shorter version of wait_for_link_text_visible() """5226 self.__check_scope()5227 if not timeout:5228 timeout = settings.LARGE_TIMEOUT5229 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5230 timeout = self.__get_new_timeout(timeout)5231 return self.wait_for_link_text_visible(link_text, timeout=timeout)5232 def find_link_text(self, link_text, timeout=None):5233 """ Same as wait_for_link_text_visible() - returns the element """5234 self.__check_scope()5235 if not timeout:5236 timeout = settings.LARGE_TIMEOUT5237 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5238 timeout = self.__get_new_timeout(timeout)5239 return self.wait_for_link_text_visible(link_text, timeout=timeout)5240 def assert_link_text(self, link_text, timeout=None):5241 """ Similar to wait_for_link_text_visible(), but returns nothing.5242 As above, will raise an exception if nothing can be found.5243 Returns True if successful. Default timeout = SMALL_TIMEOUT. """5244 self.__check_scope()5245 if not timeout:5246 timeout = settings.SMALL_TIMEOUT5247 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5248 timeout = self.__get_new_timeout(timeout)5249 self.wait_for_link_text_visible(link_text, timeout=timeout)5250 if self.demo_mode:5251 a_t = "ASSERT LINK TEXT"5252 if self._language != "English":5253 from seleniumbase.fixtures.words import SD5254 a_t = SD.translate_assert_link_text(self._language)5255 messenger_post = ("%s: {%s}" % (a_t, link_text))5256 self.__highlight_with_assert_success(5257 messenger_post, link_text, by=By.LINK_TEXT)5258 return True5259 def wait_for_partial_link_text(self, partial_link_text, timeout=None):5260 self.__check_scope()5261 if not timeout:5262 timeout = settings.LARGE_TIMEOUT5263 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5264 timeout = self.__get_new_timeout(timeout)5265 return self.wait_for_element_visible(5266 partial_link_text, by=By.PARTIAL_LINK_TEXT, timeout=timeout)5267 def find_partial_link_text(self, partial_link_text, timeout=None):5268 """ Same as wait_for_partial_link_text() - returns the element """5269 self.__check_scope()5270 if not timeout:5271 timeout = settings.LARGE_TIMEOUT5272 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5273 timeout = self.__get_new_timeout(timeout)5274 return self.wait_for_partial_link_text(5275 partial_link_text, timeout=timeout)5276 def assert_partial_link_text(self, partial_link_text, timeout=None):5277 """ Similar to wait_for_partial_link_text(), but returns nothing.5278 As above, will raise an exception if nothing can be found.5279 Returns True if successful. Default timeout = SMALL_TIMEOUT. """5280 self.__check_scope()5281 if not timeout:5282 timeout = settings.SMALL_TIMEOUT5283 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5284 timeout = self.__get_new_timeout(timeout)5285 self.wait_for_partial_link_text(partial_link_text, timeout=timeout)5286 if self.demo_mode:5287 a_t = "ASSERT PARTIAL LINK TEXT"5288 if self._language != "English":5289 from seleniumbase.fixtures.words import SD5290 a_t = SD.translate_assert_link_text(self._language)5291 messenger_post = ("%s: {%s}" % (a_t, partial_link_text))5292 self.__highlight_with_assert_success(5293 messenger_post, partial_link_text, by=By.PARTIAL_LINK_TEXT)5294 return True5295 ############5296 def wait_for_element_absent(self, selector, by=By.CSS_SELECTOR,5297 timeout=None):5298 """ Waits for an element to no longer appear in the HTML of a page.5299 A hidden element still counts as appearing in the page HTML.5300 If an element with "hidden" status is acceptable,5301 use wait_for_element_not_visible() instead. """5302 self.__check_scope()5303 if not timeout:5304 timeout = settings.LARGE_TIMEOUT5305 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5306 timeout = self.__get_new_timeout(timeout)5307 selector, by = self.__recalculate_selector(selector, by)5308 return page_actions.wait_for_element_absent(5309 self.driver, selector, by, timeout)5310 def assert_element_absent(self, selector, by=By.CSS_SELECTOR,5311 timeout=None):5312 """ Similar to wait_for_element_absent() - returns nothing.5313 As above, will raise an exception if the element stays present.5314 Returns True if successful. Default timeout = SMALL_TIMEOUT. """5315 self.__check_scope()5316 if not timeout:5317 timeout = settings.SMALL_TIMEOUT5318 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5319 timeout = self.__get_new_timeout(timeout)5320 self.wait_for_element_absent(selector, by=by, timeout=timeout)5321 return True5322 ############5323 def wait_for_element_not_visible(self, selector, by=By.CSS_SELECTOR,5324 timeout=None):5325 """ Waits for an element to no longer be visible on a page.5326 The element can be non-existent in the HTML or hidden on the page5327 to qualify as not visible. """5328 self.__check_scope()5329 if not timeout:5330 timeout = settings.LARGE_TIMEOUT5331 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5332 timeout = self.__get_new_timeout(timeout)5333 selector, by = self.__recalculate_selector(selector, by)5334 return page_actions.wait_for_element_not_visible(5335 self.driver, selector, by, timeout)5336 def assert_element_not_visible(self, selector, by=By.CSS_SELECTOR,5337 timeout=None):5338 """ Similar to wait_for_element_not_visible() - returns nothing.5339 As above, will raise an exception if the element stays visible.5340 Returns True if successful. Default timeout = SMALL_TIMEOUT. """5341 self.__check_scope()5342 if not timeout:5343 timeout = settings.SMALL_TIMEOUT5344 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5345 timeout = self.__get_new_timeout(timeout)5346 self.wait_for_element_not_visible(selector, by=by, timeout=timeout)5347 return True5348 ############5349 def wait_for_text_not_visible(self, text, selector="html",5350 by=By.CSS_SELECTOR,5351 timeout=None):5352 self.__check_scope()5353 if not timeout:5354 timeout = settings.LARGE_TIMEOUT5355 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5356 timeout = self.__get_new_timeout(timeout)5357 selector, by = self.__recalculate_selector(selector, by)5358 return page_actions.wait_for_text_not_visible(5359 self.driver, text, selector, by, timeout)5360 def assert_text_not_visible(self, text, selector="html",5361 by=By.CSS_SELECTOR,5362 timeout=None):5363 """ Similar to wait_for_text_not_visible()5364 Raises an exception if the element or the text is not found.5365 Returns True if successful. Default timeout = SMALL_TIMEOUT. """5366 self.__check_scope()5367 if not timeout:5368 timeout = settings.SMALL_TIMEOUT5369 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5370 timeout = self.__get_new_timeout(timeout)5371 self.wait_for_text_not_visible(text, selector, by=by, timeout=timeout)5372 ############5373 def wait_for_and_accept_alert(self, timeout=None):5374 self.__check_scope()5375 if not timeout:5376 timeout = settings.LARGE_TIMEOUT5377 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5378 timeout = self.__get_new_timeout(timeout)5379 return page_actions.wait_for_and_accept_alert(self.driver, timeout)5380 def wait_for_and_dismiss_alert(self, timeout=None):5381 self.__check_scope()5382 if not timeout:5383 timeout = settings.LARGE_TIMEOUT5384 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5385 timeout = self.__get_new_timeout(timeout)5386 return page_actions.wait_for_and_dismiss_alert(self.driver, timeout)5387 def wait_for_and_switch_to_alert(self, timeout=None):5388 self.__check_scope()5389 if not timeout:5390 timeout = settings.LARGE_TIMEOUT5391 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5392 timeout = self.__get_new_timeout(timeout)5393 return page_actions.wait_for_and_switch_to_alert(self.driver, timeout)5394 ############5395 def accept_alert(self, timeout=None):5396 """ Same as wait_for_and_accept_alert(), but smaller default T_O """5397 self.__check_scope()5398 if not timeout:5399 timeout = settings.SMALL_TIMEOUT5400 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5401 timeout = self.__get_new_timeout(timeout)5402 return page_actions.wait_for_and_accept_alert(self.driver, timeout)5403 def dismiss_alert(self, timeout=None):5404 """ Same as wait_for_and_dismiss_alert(), but smaller default T_O """5405 self.__check_scope()5406 if not timeout:5407 timeout = settings.SMALL_TIMEOUT5408 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5409 timeout = self.__get_new_timeout(timeout)5410 return page_actions.wait_for_and_dismiss_alert(self.driver, timeout)5411 def switch_to_alert(self, timeout=None):5412 """ Same as wait_for_and_switch_to_alert(), but smaller default T_O """5413 self.__check_scope()5414 if not timeout:5415 timeout = settings.SMALL_TIMEOUT5416 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5417 timeout = self.__get_new_timeout(timeout)5418 return page_actions.wait_for_and_switch_to_alert(self.driver, timeout)5419 ############5420 def __assert_eq(self, *args, **kwargs):5421 """ Minified assert_equal() using only the list diff. """5422 minified_exception = None5423 try:5424 self.assertEqual(*args, **kwargs)5425 except Exception as e:5426 str_e = str(e)5427 minified_exception = "\nAssertionError:\n"5428 lines = str_e.split('\n')5429 countdown = 35430 countdown_on = False5431 for line in lines:5432 if countdown_on:5433 minified_exception += line + '\n'5434 countdown = countdown - 15435 if countdown == 0:5436 countdown_on = False5437 elif line.startswith('F'):5438 countdown_on = True5439 countdown = 35440 minified_exception += line + '\n'5441 elif line.startswith('+') or line.startswith('-'):5442 minified_exception += line + '\n'5443 elif line.startswith('?'):5444 minified_exception += line + '\n'5445 elif line.strip().startswith('*'):5446 minified_exception += line + '\n'5447 if minified_exception:5448 raise Exception(minified_exception)5449 def check_window(self, name="default", level=0, baseline=False):5450 """ *** Automated Visual Testing with SeleniumBase ***5451 The first time a test calls self.check_window() for a unique "name"5452 parameter provided, it will set a visual baseline, meaning that it5453 creates a folder, saves the URL to a file, saves the current window5454 screenshot to a file, and creates the following three files5455 with the listed data saved:5456 tags_level1.txt -> HTML tags from the window5457 tags_level2.txt -> HTML tags + attributes from the window5458 tags_level3.txt -> HTML tags + attributes/values from the window5459 Baseline folders are named based on the test name and the name5460 parameter passed to self.check_window(). The same test can store5461 multiple baseline folders.5462 If the baseline is being set/reset, the "level" doesn't matter.5463 After the first run of self.check_window(), it will compare the5464 HTML tags of the latest window to the one from the initial run.5465 Here's how the level system works:5466 * level=0 ->5467 DRY RUN ONLY - Will perform a comparison to the baseline, and5468 print out any differences that are found, but5469 won't fail the test even if differences exist.5470 * level=1 ->5471 HTML tags are compared to tags_level1.txt5472 * level=2 ->5473 HTML tags are compared to tags_level1.txt and5474 HTML tags/attributes are compared to tags_level2.txt5475 * level=3 ->5476 HTML tags are compared to tags_level1.txt and5477 HTML tags + attributes are compared to tags_level2.txt and5478 HTML tags + attributes/values are compared to tags_level3.txt5479 As shown, Level-3 is the most strict, Level-1 is the least strict.5480 If the comparisons from the latest window to the existing baseline5481 don't match, the current test will fail, except for Level-0 tests.5482 You can reset the visual baseline on the command line by using:5483 --visual_baseline5484 As long as "--visual_baseline" is used on the command line while5485 running tests, the self.check_window() method cannot fail because5486 it will rebuild the visual baseline rather than comparing the html5487 tags of the latest run to the existing baseline. If there are any5488 expected layout changes to a website that you're testing, you'll5489 need to reset the baseline to prevent unnecessary failures.5490 self.check_window() will fail with "Page Domain Mismatch Failure"5491 if the page domain doesn't match the domain of the baseline.5492 If you want to use self.check_window() to compare a web page to5493 a later version of itself from within the same test run, you can5494 add the parameter "baseline=True" to the first time you call5495 self.check_window() in a test to use that as the baseline. This5496 only makes sense if you're calling self.check_window() more than5497 once with the same name parameter in the same test.5498 Automated Visual Testing with self.check_window() is not very5499 effective for websites that have dynamic content that changes5500 the layout and structure of web pages. For those, you're much5501 better off using regular SeleniumBase functional testing.5502 Example usage:5503 self.check_window(name="testing", level=0)5504 self.check_window(name="xkcd_home", level=1)5505 self.check_window(name="github_page", level=2)5506 self.check_window(name="wikipedia_page", level=3)5507 """5508 self.__check_scope()5509 if level == "0":5510 level = 05511 if level == "1":5512 level = 15513 if level == "2":5514 level = 25515 if level == "3":5516 level = 35517 if level != 0 and level != 1 and level != 2 and level != 3:5518 raise Exception('Parameter "level" must be set to 0, 1, 2, or 3!')5519 if self.demo_mode:5520 raise Exception(5521 "WARNING: Using Demo Mode will break layout tests "5522 "that use the check_window() method due to custom "5523 "HTML edits being made on the page!\n"5524 "Please rerun without using Demo Mode!")5525 module = self.__class__.__module__5526 if '.' in module and len(module.split('.')[-1]) > 1:5527 module = module.split('.')[-1]5528 test_id = "%s.%s" % (module, self._testMethodName)5529 if not name or len(name) < 1:5530 name = "default"5531 name = str(name)5532 from seleniumbase.core import visual_helper5533 visual_helper.visual_baseline_folder_setup()5534 baseline_dir = constants.VisualBaseline.STORAGE_FOLDER5535 visual_baseline_path = baseline_dir + "/" + test_id + "/" + name5536 page_url_file = visual_baseline_path + "/page_url.txt"5537 screenshot_file = visual_baseline_path + "/screenshot.png"5538 level_1_file = visual_baseline_path + "/tags_level_1.txt"5539 level_2_file = visual_baseline_path + "/tags_level_2.txt"5540 level_3_file = visual_baseline_path + "/tags_level_3.txt"5541 set_baseline = False5542 if baseline or self.visual_baseline:5543 set_baseline = True5544 if not os.path.exists(visual_baseline_path):5545 set_baseline = True5546 try:5547 os.makedirs(visual_baseline_path)5548 except Exception:5549 pass # Only reachable during multi-threaded test runs5550 if not os.path.exists(page_url_file):5551 set_baseline = True5552 if not os.path.exists(screenshot_file):5553 set_baseline = True5554 if not os.path.exists(level_1_file):5555 set_baseline = True5556 if not os.path.exists(level_2_file):5557 set_baseline = True5558 if not os.path.exists(level_3_file):5559 set_baseline = True5560 page_url = self.get_current_url()5561 soup = self.get_beautiful_soup()5562 html_tags = soup.body.find_all()5563 level_1 = [[tag.name] for tag in html_tags]5564 level_1 = json.loads(json.dumps(level_1)) # Tuples become lists5565 level_2 = [[tag.name, sorted(tag.attrs.keys())] for tag in html_tags]5566 level_2 = json.loads(json.dumps(level_2)) # Tuples become lists5567 level_3 = [[tag.name, sorted(tag.attrs.items())] for tag in html_tags]5568 level_3 = json.loads(json.dumps(level_3)) # Tuples become lists5569 if set_baseline:5570 self.save_screenshot("screenshot.png", visual_baseline_path)5571 out_file = codecs.open(page_url_file, "w+", encoding="utf-8")5572 out_file.writelines(page_url)5573 out_file.close()5574 out_file = codecs.open(level_1_file, "w+", encoding="utf-8")5575 out_file.writelines(json.dumps(level_1))5576 out_file.close()5577 out_file = codecs.open(level_2_file, "w+", encoding="utf-8")5578 out_file.writelines(json.dumps(level_2))5579 out_file.close()5580 out_file = codecs.open(level_3_file, "w+", encoding="utf-8")5581 out_file.writelines(json.dumps(level_3))5582 out_file.close()5583 if not set_baseline:5584 f = open(page_url_file, 'r')5585 page_url_data = f.read().strip()5586 f.close()5587 f = open(level_1_file, 'r')5588 level_1_data = json.loads(f.read())5589 f.close()5590 f = open(level_2_file, 'r')5591 level_2_data = json.loads(f.read())5592 f.close()5593 f = open(level_3_file, 'r')5594 level_3_data = json.loads(f.read())5595 f.close()5596 domain_fail = (5597 "\nPage Domain Mismatch Failure: "5598 "Current Page Domain doesn't match the Page Domain of the "5599 "Baseline! Can't compare two completely different sites! "5600 "Run with --visual_baseline to reset the baseline!")5601 level_1_failure = (5602 "\n*\n*** Exception: <Level 1> Visual Diff Failure:\n"5603 "* HTML tags don't match the baseline!")5604 level_2_failure = (5605 "\n*\n*** Exception: <Level 2> Visual Diff Failure:\n"5606 "* HTML tag attribute names don't match the baseline!")5607 level_3_failure = (5608 "\n*\n*** Exception: <Level 3> Visual Diff Failure:\n"5609 "* HTML tag attribute values don't match the baseline!")5610 page_domain = self.get_domain_url(page_url)5611 page_data_domain = self.get_domain_url(page_url_data)5612 unittest.TestCase.maxDiff = 10005613 if level != 0:5614 self.assertEqual(page_data_domain, page_domain, domain_fail)5615 unittest.TestCase.maxDiff = None5616 if level == 3:5617 self.__assert_eq(level_3_data, level_3, level_3_failure)5618 if level == 2:5619 self.__assert_eq(level_2_data, level_2, level_2_failure)5620 unittest.TestCase.maxDiff = 10005621 if level == 1:5622 self.__assert_eq(level_1_data, level_1, level_1_failure)5623 unittest.TestCase.maxDiff = None5624 if level == 0:5625 try:5626 unittest.TestCase.maxDiff = 10005627 self.assertEqual(5628 page_domain, page_data_domain, domain_fail)5629 unittest.TestCase.maxDiff = None5630 self.__assert_eq(level_3_data, level_3, level_3_failure)5631 except Exception as e:5632 print(e) # Level-0 Dry Run (Only print the differences)5633 ############5634 def __get_new_timeout(self, timeout):5635 """ When using --timeout_multiplier=#.# """5636 self.__check_scope()5637 try:5638 timeout_multiplier = float(self.timeout_multiplier)5639 if timeout_multiplier <= 0.5:5640 timeout_multiplier = 0.55641 timeout = int(math.ceil(timeout_multiplier * timeout))5642 return timeout5643 except Exception:5644 # Wrong data type for timeout_multiplier (expecting int or float)5645 return timeout5646 ############5647 def __check_scope(self):5648 if hasattr(self, 'browser'): # self.browser stores the type of browser5649 return # All good: setUp() already initialized variables in "self"5650 else:5651 from seleniumbase.common.exceptions import OutOfScopeException5652 message = (5653 "\n It looks like you are trying to call a SeleniumBase method"