"""The press and marketing page."""
from datetime import datetime
from re import sub as substitute
from pypom import Region
from requests import get, head
from selenium.common.exceptions import WebDriverException
from selenium.webdriver.common.by import By
from pages.web.base import WebBase
from utils.utilities import Utility, go_to_
from utils.web import Web
[docs]class Release(Region):
"""An OpenStax press release snippet."""
_byline_locator = (By.CSS_SELECTOR, '.byline')
_date_locator = (By.CSS_SELECTOR, '.date')
_headline_locator = (By.CSS_SELECTOR, '.headline a')
_excerpt_locator = (By.CSS_SELECTOR, '.excerpt')
_continue_reading_locator = (By.CSS_SELECTOR, _excerpt_locator[1] + ' a')
@property
def byline(self):
"""Return the byline text."""
return self.find_element(*self._byline_locator).text
@property
def author(self):
"""Return the name from the byline."""
return self.byline.split('|')[0].strip()
@property
def date(self):
"""Return a timezone-aware date of release."""
return datetime.strptime(
self.find_element(*self._date_locator).text + " +0000",
"%b %d, %Y %z")
@property
def headline(self):
"""Return the press release headline."""
return self.find_element(*self._headline_locator).text.strip()
@property
def excerpt(self):
"""Return the excerpt."""
try:
return self.find_element(*self._excerpt_locator).text.strip()
except WebDriverException:
return ''
@property
def continue_reading(self):
"""Return the 'Continue reading' link."""
try:
return self.find_element(*self._continue_reading_locator)
except WebDriverException:
return None
[docs] def select(self):
"""Select a press release to view the entire text."""
if self.continue_reading:
append = self.continue_reading.get_attribute('href').split('/')[-1]
Utility.click_option(self.driver, element=self.continue_reading)
return go_to_(PressRelease(driver=self.driver,
base_url=self.page.base_url,
article=append))
[docs]class Press(WebBase):
"""The press and marketing page."""
URL_TEMPLATE = '/press'
HEADLINE = ' .headline-container:not([hidden]) .press-excerpt'
MAIN = '.main-body '
MOBILE = '.mobile-view '
FIRST = ' .nav-buttons div:first-child'
LAST = ' .nav-buttons div:last-child'
_hero_quote_selector = '.hero h1'
_press_releases_selector = '.press-releases'
_sidebar_selector = '.sidebar'
_news_mentions_selector = '.news-mentions'
_image_locator = (By.CSS_SELECTOR, 'img')
_title_locator = (By.CSS_SELECTOR, _hero_quote_selector)
_see_toggle_locator = (By.CSS_SELECTOR, '.see')
_menu_select_locator = (By.CSS_SELECTOR, '.selector-button')
_menu_item_locator = (By.CSS_SELECTOR, '.mobile-selector [role=menuitem]')
_current_menuitem_locator = (By.CSS_SELECTOR, '.mobile-selector span')
_mission_statement_locator = (
By.CSS_SELECTOR, '[data-html^=missionStatement]')
_viewing_fewer_locator = (By.CSS_SELECTOR, '.fewer:not([hidden])')
_releases_full_excerpt_locator = (
By.CSS_SELECTOR, '.press-releases .fewer .press-excerpt')
_releases_full_locator = (
By.CSS_SELECTOR, '.press-releases .more' + HEADLINE)
_releases_mobile_locator = (
By.CSS_SELECTOR, '.press-mobile .releases' + HEADLINE)
_newer_releases_full_locator = (By.CSS_SELECTOR, MAIN + '.more' + FIRST)
_newer_releases_mobile_locator = (
By.CSS_SELECTOR, MOBILE + '.more' + FIRST)
_older_releases_full_locator = (By.CSS_SELECTOR, MAIN + '.more' + LAST)
_older_releases_mobile_locator = (By.CSS_SELECTOR, MOBILE + 'more' + LAST)
_mentions_full_locator = (
By.CSS_SELECTOR, MAIN + _news_mentions_selector + HEADLINE)
_mentions_mobile_locator = (
By.CSS_SELECTOR, MOBILE + '.mentions' + HEADLINE)
_newer_mentions_full_locator = (
By.CSS_SELECTOR, MAIN + _news_mentions_selector + FIRST)
_newer_mentions_mobile_locator = (
By.CSS_SELECTOR, MOBILE + '.mentions' + FIRST)
_older_mentions_full_locator = (
By.CSS_SELECTOR, MAIN + _news_mentions_selector + LAST)
_older_mentions_mobile_locator = (
By.CSS_SELECTOR, MOBILE + '.mentions' + LAST)
_contact_full_locator = (By.CSS_SELECTOR, MAIN + '.contact')
_contact_mobile_locator = (By.CSS_SELECTOR, MOBILE + '.contact')
_social_full_locator = (By.CSS_SELECTOR, MAIN + '.find-us a')
_social_mobile_locator = (By.CSS_SELECTOR, MOBILE + '.find-us a')
_press_kit_full_locator = (By.CSS_SELECTOR, MAIN + '[href*=press_kit]')
_press_kit_mobile_locator = (By.CSS_SELECTOR, MOBILE + '[href*=press_kit]')
_experts_full_locator = (By.CSS_SELECTOR, MAIN + '.booking')
_experts_mobile_locator = (By.CSS_SELECTOR, MOBILE + '.booking')
@property
def loaded(self):
"""Return True when the four root elements are found."""
locator = (
By.CSS_SELECTOR,
'{hero} , {press} , {sidebar} , {news}'
.format(hero=self._hero_quote_selector,
press=self._press_releases_selector,
sidebar=self._sidebar_selector,
news=self._news_mentions_selector))
try:
merged = self.find_elements(*locator)
sections_found = len(merged) == 4
images_visible = Utility.is_image_visible(
self.driver, locator=self._image_locator)
contact_found = (self.contact.name
if not isinstance(self.contact, list)
else self.contact[0].name)
return (super().loaded and
sections_found and
images_visible and
(contact_found or self.is_phone))
except Exception:
return False
[docs] def is_displayed(self):
"""Return True if the heading is displayed."""
return self.find_element(*self._title_locator).is_displayed()
@property
def title(self):
"""Return the title."""
return self.find_element(*self._title_locator).text
[docs] def select(self, option):
"""Select a mobile menu option."""
if option == self.current_menu_item or not self.is_phone:
return self
menu = self.find_element(*self._menu_select_locator)
Utility.click_option(self.driver, element=menu)
for section in self.menu_options:
if section.text == option:
Utility.click_option(self.driver, element=section)
return self
raise(ValueError('"{0}" is not an available section'.format(option)))
@property
def menu_options(self):
"""Return the list of selection options."""
return self.find_elements(*self._menu_item_locator)
@property
def current_menu_item(self):
"""Return the currently selected mobile menu option."""
return self.find_element(*self._current_menuitem_locator).text
@property
def releases(self):
"""Access the press releases."""
if self.is_phone:
locator = self._releases_mobile_locator
elif self.viewing_fewer_releases:
locator = self._releases_full_excerpt_locator
else:
locator = self._releases_full_locator
return [Release(self, release)
for release in self.find_elements(*locator)]
@property
def viewing_fewer_releases(self):
"""Return True if viewing the top two press releases."""
return (self.find_elements(*self._viewing_fewer_locator) and
not self.is_phone)
[docs] def see_more_releases(self):
"""Toggle the fewer / more press releases switch."""
switch = self.find_element(*self._see_toggle_locator)
Utility.click_option(self.driver, element=switch)
return self
[docs] def see_fewer_releases(self):
"""Toggle the fewer / more press releases switch."""
return self.see_more_releases()
[docs] def view_older_releases(self):
"""View older OpenStax press releases."""
return self._move_pagination(
self._older_releases_mobile_locator,
self._older_releases_full_locator)
[docs] def view_newer_releases(self):
"""View newer OpenStax press releases."""
return self._move_pagination(
self._newer_releases_mobile_locator,
self._newer_releases_full_locator)
@property
def mentions(self):
"""Access the news articles mentioning OpenStax."""
if self.is_phone:
locator = self._mentions_mobile_locator
else:
locator = self._mentions_full_locator
return [self.Mention(self, article)
for article in self.find_elements(*locator)]
[docs] def view_older_mentions(self):
"""View older news articles mentioning OpenStax."""
return self._move_pagination(
self._older_mentions_mobile_locator,
self._older_mentions_full_locator)
[docs] def view_newer_mentions(self):
"""View newer news articles mentioning OpenStax."""
return self._move_pagination(
self._newer_mentions_mobile_locator,
self._newer_mentions_full_locator)
@property
def mission_statements(self):
"""Return the list of mission statements."""
return [substitute(r'<\/?b>', '', statement.get_attribute('innerHTML'))
for statement
in self.find_elements(*self._mission_statement_locator)]
@property
def mission_displayed(self):
"""Return True if the mission statements are displayed."""
return self.driver.execute_script(
'return document.querySelector("{0}").clientHeight > 0;'
.format(self._sidebar_selector))
@property
def contact(self):
"""Return the press contact information."""
if self.is_phone:
locator = self._contact_mobile_locator
else:
locator = self._contact_full_locator
contacts = self.find_elements(*locator)
assert(contacts), 'No contact person/people found'
if len(contacts) > 1:
return [self.Contact(self, person)
for person in contacts]
return self.Contact(self, contacts[0])
@property
def social(self):
"""Access the OpenStax social pages."""
if self.is_phone:
locator = self._social_mobile_locator
else:
locator = self._social_full_locator
return [self.Social(self, company)
for company in self.find_elements(*locator)]
@property
def press_kit(self):
"""Return the press kit download button."""
if self.is_phone:
locator = self._press_kit_mobile_locator
else:
locator = self._press_kit_full_locator
return self.find_element(*locator)
[docs] def check_press_kit(self):
"""Request the HEAD of the press kit download."""
return head(self.press_kit.get_attribute('href'))
@property
def experts(self):
"""Access the OpenStax experts cards."""
if self.is_phone:
locator = self._experts_mobile_locator
else:
locator = self._experts_full_locator
return [self.Expert(self, person)
for person in self.find_elements(*locator)]
def _move_pagination(self, mobile, full):
"""Pagination helper for press releases and news mentions."""
if self.is_phone:
switch = self.find_element(*mobile)
else:
switch = self.find_element(*full)
if 'presentation' not in switch.get_attribute('role'):
# there is another pagination group of older or newer links
Utility.click_option(self.driver, element=switch)
return self
[docs] class Mention(Region):
"""A news article mentioning OpenStax."""
_logo_locator = (By.CSS_SELECTOR, 'img')
_source_locator = (By.CSS_SELECTOR, '.source')
_byline_locator = (By.CSS_SELECTOR, '.byline .date')
_headline_locator = (By.CSS_SELECTOR, '.headline a')
@property
def logo(self):
"""Return the logo."""
return self.find_element(*self._logo_locator)
@property
def source(self):
"""Return the source organization name."""
return self.find_element(*self._source_locator).text.strip()
@property
def date(self):
"""Return a timezone-aware date of publication."""
return datetime.strptime(
self.find_element(*self._byline_locator).text
.split('-', 1)[-1].strip() + ' +0000',
'%b %d, %Y %z')
@property
def headline_element(self):
"""Return the headline element."""
return self.find_element(*self._headline_locator)
@property
def headline(self):
"""Return the article headline."""
return self.headline_element.text.strip()
[docs] def select(self):
"""Follow the article link."""
Utility.switch_to(self.driver, element=self.headline_element)
return self.driver
[docs] def check_article(self):
"""Check the HEAD request for the article URL."""
return head(self.url)
@property
def url(self):
"""Return the article URL."""
return self.headline_element.get_attribute('href')
[docs] class Social(Region):
"""A social media option."""
_icon_locator = (By.CSS_SELECTOR, 'svg')
@property
def name(self):
"""Return the Social media company name."""
destination_url = self.url
for company in Web.MEDIA:
if company in destination_url:
return Web.MEDIA.get(company)
@property
def url_name(self):
"""Return the case-lowered company name found in the URL."""
return self.name.lower()
@property
def url(self):
"""Return the link URL."""
return self.root.get_attribute('href')
@property
def select(self):
"""Go to the OpenStax social media page."""
return Utility.switch_to(self.driver, element=self.root)
[docs] class Expert(Region):
"""An OpenStax expert."""
_card_locator = (By.CSS_SELECTOR, '.card')
_portrait_locator = (By.CSS_SELECTOR, 'img')
_name_locator = (By.CSS_SELECTOR, '.name')
_role_locator = (By.CSS_SELECTOR, _name_locator[1] + ' ~ div')
_bio_locator = (By.CSS_SELECTOR, '[data-html=bio]')
[docs] def is_displayed(self):
"""Return True if the portrait is loaded and in the frame."""
Utility.scroll_to(self.driver, element=self.portrait, shift=-20)
card = self.find_element(*self._card_locator)
return (Utility.is_image_visible(
self.driver, image=self.portrait) and
Utility.in_viewport(
self.driver, element=card, display_marks=True))
@property
def portrait(self):
"""Return the expert's picture."""
return self.find_element(*self._portrait_locator)
@property
def has_portrait(self):
"""Return True if the portrait exists."""
return Utility.is_image_visible(self.driver, image=self.portrait)
@property
def name(self):
"""Return the expert's name."""
return self.find_element(*self._name_locator).text.strip()
@property
def role(self):
"""Return the expert's role or job description."""
return self.find_element(*self._role_locator).text.strip()
@property
def bio(self):
"""Return the expert's short biography."""
return self.find_element(*self._bio_locator).text.strip()
[docs]class PressRelease(WebBase):
"""A press release page."""
URL_TEMPLATE = '/press/{article}'
_article_locator = (By.CSS_SELECTOR, '.page > .article')
_headline_locator = (By.CSS_SELECTOR, 'article h1')
_author_locator = (By.CSS_SELECTOR, '.author')
_date_locator = (By.CSS_SELECTOR, '.date')
_content_locator = (By.CSS_SELECTOR, '.body p')
_see_toggle_locator = (By.CSS_SELECTOR, '.see')
_viewing_fewer_locator = (By.CSS_SELECTOR, '.fewer:not([hidden])')
_newer_releases_locator = (By.CSS_SELECTOR, '.nav-buttons div:first-child')
_older_releases_locator = (By.CSS_SELECTOR, '.nav-buttons div:last-child')
_releases_excerpt_locator = (
By.CSS_SELECTOR, '.fewer .press-excerpt')
_releases_locator = (
By.CSS_SELECTOR,
'.more .headline-container:not([hidden]) .press-excerpt')
@property
def loaded(self):
"""Return True when the press release text is displayed."""
return super().loaded and bool(self.content)
[docs] def is_displayed(self):
"""Return True if the press release sections are available."""
return self.headline and self.author and self.date and self.content
@property
def is_an_article(self):
"""Return True if the page is a press release article."""
return bool(self.find_elements(*self._article_locator))
@property
def headline(self):
"""Return the headline text."""
return self.find_element(*self._headline_locator).text.strip()
@property
def author(self):
"""Return the press release author."""
return self.find_element(*self._author_locator).text.strip()
@property
def date(self):
"""Return a timezone-aware date of publication."""
return datetime.strptime(
self.find_element(*self._date_locator).text.strip() + ' +0000',
'%b %d, %Y %z')
@property
def content(self):
"""Return the press release content."""
return [paragraph.text.strip()
for paragraph in self.find_elements(*self._content_locator)]
@property
def other_releases(self):
"""Access the press releases."""
locator = (self._releases_excerpt_locator
if self.viewing_fewer_releases
else self._releases_locator)
return [Release(self, release)
for release in self.find_elements(*locator)]
@property
def viewing_fewer_releases(self):
"""Return True if viewing the top two press releases."""
return bool(self.find_elements(*self._viewing_fewer_locator))
[docs] def see_more_releases(self):
"""Toggle the fewer / more press releases switch."""
switch = self.find_element(*self._see_toggle_locator)
Utility.click_option(self.driver, element=switch)
return self
[docs] def see_fewer_releases(self):
"""Toggle the fewer / more press releases switch."""
return self.see_more_releases()
[docs] def view_older_releases(self):
"""View older OpenStax press releases."""
if not self.viewing_fewer_releases:
switch = self.find_element(*self._older_releases_locator)
if 'presentation' not in switch.get_attribute('role'):
# there is another pagination group of older or newer links
Utility.click_option(self.driver, element=switch)
return self
[docs] def view_newer_releases(self):
"""View newer OpenStax press releases."""
if not self.viewing_fewer_releases:
switch = self.find_element(*self._newer_releases_locator)
if 'presentation' not in switch.get_attribute('role'):
# there is another pagination group of older or newer links
Utility.click_option(self.driver, element=switch)
return self