Ejemplo n.º 1
0
    def is_chart_displayed(self, chart: str) -> bool:
        """Boolean check for whether a specific chart is displayed on the Reports page.

        :param chart: The name of a chart on the Reports page.
        """
        groomed_chart: str
        if chart == 'Origins & Destinations':
            groomed_chart = f'chart-ride-{chart.lower().replace(" ", "-")}'
        else:
            groomed_chart = f'chart-{chart.lower().replace(" ", "-")}'
        element: WebElement = self.driver.wait_until_visible(
            *Selectors.data_id(groomed_chart))

        return element.is_displayed()
class PassengerDetailsModal(Component):
    """Objects and methods for the Passenger Details Modal component."""

    ROOT_LOCATOR: Selector = Selectors.data_id('passenger-details-container')
    _next_button: Selector = Selectors.data_id('next-button')
    _capacity_input: Selector = Selectors.data_id('capacity-select')
    _wheelchair_container: Selector = Selectors.data_id('wheelchair-container')

    @property
    def next_button(self) -> WebElement:
        return self.container.find_element(*self._next_button)

    @property
    def capacity_input(self) -> WebElement:
        return self.container.find_element(*self._capacity_input)

    @property
    def wheelchair_container(self) -> WebElement:
        return self.container.find_element(*self._wheelchair_container)

    def wheelchair_select(self) -> None:
        """Select the wheelchair input for a ride."""
        self.wheelchair_container.click()
Ejemplo n.º 3
0
class KebabMenu(Component):
    """Objects and methods for the Kebab Menu component."""

    ROOT_LOCATOR: Selector = Selectors.data_id('kebab-menu-container')
    _delete_button: Selector = Selectors.data_id('delete-group')
    _edit_button: Selector = Selectors.data_id('edit-group')
    _manage_button: Selector = Selectors.data_id('manage-users-item')

    @property
    def delete_button(self) -> WebElement:
        return self.container.find_element(*self._delete_button)

    @property
    def edit_button(self) -> WebElement:
        return self.container.find_element(*self._edit_button)

    @property
    def manage_button(self) -> WebElement:
        return self.container.find_element(*self._manage_button)

    def delete_group(self) -> object:
        """Open the deletion modal for a group row."""
        self.delete_button.click()

        return DeletionModal(self).wait_for_component_to_be_visible()

    def edit_group(self) -> object:
        """Open the edit group form for a group row."""
        self.edit_button.click()

        return GroupForm(self).wait_for_component_to_be_visible()

    def manage_group_users(self) -> object:
        """Open the edit group form for a group row."""
        self.manage_button.click()

        return GroupUserForm(self).wait_for_component_to_be_visible()
Ejemplo n.º 4
0
class Rides(Base):
    """Rides Page objects and methods for the OnDemand Web application."""

    URL_PATH = f'{Base.URL_PATH}/rides'
    ROOT_LOCATOR: Selector = Selectors.data_id('rides-page-container')
    _past_tab: Selector = Selectors.data_id('past')
    _tabs_container: Selector = Selectors.data_id('tabs-container')
    _upcoming_tab: Selector = Selectors.data_id('upcoming')

    @property
    def past_rides_tab(self) -> WebElement:
        return self.tabs_container.find_element(*self._past_tab)

    @property
    def rides_list(self) -> RidesList:
        return RidesList(self)

    @property
    def tabs_container(self) -> WebElement:
        return self.driver.find_element(*self._tabs_container)

    @property
    def upcoming_rides_tab(self) -> WebElement:
        return self.tabs_container.find_element(*self._upcoming_tab)
Ejemplo n.º 5
0
class LocationCard(Component):
    """Objects and methods for the Location Card component."""

    ROOT_LOCATOR: Selector = Selectors.data_id('location-card')
    _card_id: Selector = Selectors.data_id('card-id')
    _drop_off_field: Selector = Selectors.data_id('dropoff-checkbox')
    _hub_field: Selector = Selectors.data_id('hub-checkbox')
    _input: Selector = Selectors.tag('input')
    _label: Selector = Selectors.data_id('location-card-label')
    _pick_up_field: Selector = Selectors.data_id('pickup-checkbox')

    @property
    def card_id(self) -> str:
        """Return the unique address ID for a location card.

        The get_attribute method is used instead of .text as the service_id is a hidden attribute.
        """
        return self.container.find_element(
            *self._card_id).get_attribute('innerText')

    @property
    def drop_off_checkbox(self) -> WebElement:
        return self.drop_off_field.find_element(*self._input)

    @property
    def drop_off_field(self) -> WebElement:
        return self.container.find_element(*self._drop_off_field)

    @property
    def hub_checkbox(self) -> WebElement:
        return self.hub_field.find_element(*self._input)

    @property
    def hub_field(self) -> WebElement:
        return self.container.find_element(*self._hub_field)

    @property
    def label(self) -> str:
        return self.container.find_element(*self._label).text

    @property
    def pick_up_checkbox(self) -> WebElement:
        return self.pick_up_field.find_element(*self._input)

    @property
    def pick_up_field(self) -> WebElement:
        return self.container.find_element(*self._pick_up_field)

    def hub_card(self) -> WebElement:
        """Run a check to see whether a card is a hub card or not."""
        return self.container.wait_until_present(*self._hub_field)
Ejemplo n.º 6
0
class ArchiveModal(Component):
    """Objects and methods for the Service Archive Modal."""

    ROOT_LOCATOR: Selector = Selectors.data_id(
        'archive-service-modal-container')
    _cancel_button: Selector = Selectors.data_id(
        'cancel-archive-service-button')
    _confirm_button: Selector = Selectors.data_id(
        'confirm-archive-service-button')
    _message: Selector = Selectors.data_id('archive-service-message')

    @property
    def archive_message(self) -> WebElement:
        return self.container.find_element(*self._message)

    @property
    def cancel_button(self) -> WebElement:
        return self.container.find_element(*self._cancel_button)

    @property
    def confirm_button(self) -> WebElement:
        return self.container.find_element(*self._confirm_button)

    def confirm_service_archive(self) -> bool:
        """Confirm a service archive.

        Returns True if the container is no longer displayed and False if an Alert is raised. This
        method should be used with an assert statement to verify its boolean return.
        """
        self.confirm_button.click()

        try:
            self.page.driver.wait_until_not_visible(*self.ROOT_LOCATOR)
            return True
        except UnexpectedAlertPresentException:
            return False
Ejemplo n.º 7
0
class ScheduleForm(Component):
    """Objects and methods for the Schedule form."""

    ROOT_LOCATOR: Selector = Selectors.data_id('ride-schedule-container')
    _asap_ride_button: Selector = Selectors.data_id('asap-ride-button')
    _future_ride_button: Selector = Selectors.data_id('future-ride-button')
    _recurring_ride_button: Selector = Selectors.data_id(
        'recurring-ride-button')

    @property
    def asap_ride_button(self) -> WebElement:
        return self.container.find_element(*self._asap_ride_button)

    @property
    def future_ride_button(self) -> WebElement:
        return self.container.find_element(*self._future_ride_button)

    @property
    def recurring_ride_button(self) -> WebElement:
        return self.container.find_element(*self._recurring_ride_button)

    def select_ride_type(self, ride_type: str) -> None:
        """Select a ride type.

        Ride type defaults to ASAP within the OnDemand Admin application.

        :param ride_type: The type of ride to be submitted.
        """
        if ride_type == 'future':
            self.future_ride_button.click()
        elif ride_type == 'recurring':
            self.recurring_ride_button.click()
        elif ride_type != 'asap':
            raise RideTypeException(
                f'The ride type: {ride_type} is not supported.\n'
                f'Expected "asap", "future", or "recurring".', )
Ejemplo n.º 8
0
class PassengerForm(Component):
    """Objects and methods for the Passenger form."""

    ROOT_LOCATOR: Selector = Selectors.data_id('user-details-container')
    _first_name_field: Selector = Selectors.name('first_name')
    _last_name_field: Selector = Selectors.name('last_name')
    _phone_field: Selector = Selectors.name('phone')

    @property
    def first_name_field(self) -> WebElement:
        return self.container.find_element(*self._first_name_field)

    @property
    def last_name_field(self) -> WebElement:
        return self.container.find_element(*self._last_name_field)

    @property
    def phone_field(self) -> WebElement:
        return self.container.find_element(*self._phone_field)

    def fill_passenger_info_form(self, ride: dict) -> None:
        """Fill out the passenger information form.

        The ride param may be of type RecurringRide or Ride depending on the test which is being
        ran. The default type will be Ride as it is the most common data type for testing.

        :param ride: A ride yielded from a ride fixture.
        """
        try:
            rider = ride['ride']['rider']
        except KeyError:
            rider = ride['rider']

        self.first_name_field.fill(rider['first_name'])
        self.last_name_field.fill(rider['last_name'])
        self.phone_field.fill(rider['phone'])
Ejemplo n.º 9
0
class AddressesList(Component):
    """Objects and methods for the Addresses List component."""

    ROOT_LOCATOR: Selector = Selectors.data_id('address-list-container')

    @property
    def address_rows(self) -> Tuple[AddressRow, ...]:
        """Return a tuple of address rows within the list container.

        A conditional check for whether the URL contains 'admin' allows this method to be used in
        both OnDemand Web and OnDemand Admin.
        """
        rows: WebElements

        if 'admin' not in self.driver.current_url:
            rows = self.container.find_elements(*WebAddressRow.ROOT_LOCATOR)

            return tuple(WebAddressRow(self, element) for element in rows)
        rows = self.container.find_elements(*AdminAddressRow.ROOT_LOCATOR)

        return tuple(AdminAddressRow(self, element) for element in rows)

    def filter_rows(self,
                    address: dict,
                    address_label: str = None) -> Union[AddressRow, None]:
        """Filter all address rows for a match with an address label.

        :param address_label: An optional label for an address.
        :param address: An address object yielded from an address fixture.
        """
        name: str = address['name'] if address_label is None else address_label
        address_list: Tuple[AddressRow,
                            ...] = tuple(row for row in self.address_rows
                                         if name in row.data)

        if not address_list:
            return None
        return address_list[0]

    def surface_address_row(self,
                            address: dict,
                            address_label: str = None) -> AddressRow:
        """Raise a address row that matches an address label.

        :param address_label: An optional label for an address.
        :param address: An address object yielded from an address fixture.
        """
        return self.filter_rows(address, address_label=address_label)
class ServiceCardList(Component):
    """Objects and methods for the Service Card list."""

    ROOT_LOCATOR: Selector = Selectors.data_id('service-card-container')

    @property
    def service_cards(self) -> Tuple[ServiceCard, ...]:
        return tuple(
            ServiceCard(self.page, item)
            for item in self.container.find_elements(
                *ServiceCard.ROOT_LOCATOR))

    def card_archived(self, service: dict) -> bool:
        """Confirm a card has been archived.

        Returns False if the card is still displayed and True if it is no longer within the DOM.
        This method should be used with an assert statement to verify its boolean return.

        :param service: The service intended to be archived.
        """
        try:
            self.surface_service_card(service)
            return False
        except NoSuchElementException:
            return True
        except StaleElementReferenceException:
            return True

    def filter_cards(self, service: dict) -> ServiceCard:
        """Filter all service cards for a match with a service id.

        :param service: The service intended to be raised.
        """
        card_list: Tuple[ServiceCard, ...] = tuple(
            card for card in self.service_cards
            if card.service_id == service['service_id'])

        if not card_list:
            raise NoSuchElementException
        return card_list[0]

    def surface_service_card(self, service: dict) -> ServiceCard:
        """Raise a service card for later use.

        :param service: The service intended to be raised.
        """
        return self.filter_cards(service)
Ejemplo n.º 11
0
class RideCardPanel(Component):
    """Objects and methods for the ride cards panel."""

    ROOT_LOCATOR: Selector = Selectors.data_id('ride-card-container')

    @property
    def ride_cards(self) -> Tuple[RideCard, ...]:
        return tuple(
            RideCard(self, element)
            for element in self.container.find_elements(
                *RideCard.ROOT_LOCATOR))

    def filter_cards(self, ride: dict) -> RideCard:
        """Filter all ride cards for a match with a passenger name.

        :param ride: The ride yielded from a ride fixture.
        """
        rider = ride['rider']
        rider_name = f'{rider["first_name"]} {rider["last_name"]}'

        self.verify_card_created(rider_name)

        card_list: Tuple[RideCard,
                         ...] = tuple(card for card in self.ride_cards
                                      if card.rider_name == rider_name)

        if not card_list:
            raise NoSuchElementException
        return card_list[0]

    def verify_card_created(self, rider_name: str) -> None:
        """Wait until a card is located which contains a specific name.

        An increased wait time is used within this function as cards can take upward of seven
        seconds to appear within the DOM. This amount is nearly doubled when running in parallel.

        :param rider_name: The name of the rider for a Ride object.
        """
        self.container.wait_until_visible(*Selectors.text(rider_name),
                                          wait_time=10)

    def surface_ride_card(self, ride: dict) -> RideCard:
        """Validate a ride card, then raise it for later use.

        :param ride: The ride yielded from a ride fixture.
        """
        return self.filter_cards(ride)
Ejemplo n.º 12
0
class AddressForm(Component):
    """Objects and methods for the Address Form component.

    The address form is used in both editing and creating addresses. It may be accessed by selecting
    'EDIT' from an address kebab menu or by selecting the FAB button for creating a new address.
    """

    ROOT_LOCATOR: Selector = Selectors.data_id('address-form-container')
    _button_container: Selector = Selectors.data_id(
        'address-form-button-container')
    _cancel_button: Selector = Selectors.data_id('address-form-cancel-button')
    _label_input: Selector = Selectors.placeholder('Label')
    _location_input: Selector = Selectors.placeholder(
        'Search for a location...')
    _places_autocomplete_suggestion: Selector = Selectors.data_id(
        'places-autocomplete-suggestion')
    _save_button: Selector = Selectors.data_id('address-form-confirm-button')

    @property
    def button_container(self) -> WebElement:
        return self.container.find_element(*self._button_container)

    @property
    def cancel_button(self) -> WebElement:
        return self.button_container.find_element(*self._cancel_button)

    @property
    def label_input(self) -> WebElement:
        return self.container.find_element(*self._label_input)

    @property
    def location_input(self) -> WebElement:
        return self.container.find_element(*self._location_input)

    @property
    def save_button(self) -> WebElement:
        return self.button_container.find_element(*self._save_button)

    @property
    def suggestions(self) -> AutocompleteSuggestions:
        return AutocompleteSuggestions(self)

    def select_location(self, location: str) -> None:
        """Fill out an address location, then submit the location.

        :param location: The location for an address.
        """
        self.location_input.fill(location)
        self.suggestions.select_suggestion(location)
Ejemplo n.º 13
0
class RideFilters(Component):
    """Objects and methods for the Ride Filters container."""

    ROOT_LOCATOR: Selector = Selectors.data_id('ride-filters-container')
    TABS: List[str] = Admin.RIDE_FILTERS

    def select_filter(self, filter_type: str) -> None:
        """Filter all ride cards based on a selected type.

        :param filter_type: The specified ride filter.
        """
        _groomed_tab: str = filter_type.lower().replace(' ', '-')

        if filter_type not in self.TABS:
            raise FilterException
        self.container.find_element(
            *Selectors.data_id(f'ride-filter-{_groomed_tab}')).click()
Ejemplo n.º 14
0
class RideRow(Component):
    """Objects and methods for the Ride Row component."""

    ROOT_LOCATOR: Selector = Selectors.data_id('ride-list-row')
    _ride_drop_off: Selector = Selectors.data_id('ride-row-drooff-address')
    _ride_fare: Selector = Selectors.data_id('ride-row-fare')
    _ride_id: Selector = Selectors.data_id('ride-row-id')
    _ride_overview_button: Selector = Selectors.data_id('ride-overview-button')
    _ride_pick_up: Selector = Selectors.data_id('ride-row-pickup-address')
    _ride_status: Selector = Selectors.data_id('ride-row-status')

    @property
    def drop_off_address(self) -> str:
        return self.container.find_element(*self._ride_drop_off).text

    @property
    def fare(self) -> str:
        return self.container.find_element(*self._ride_fare).text

    @property
    def pick_up_address(self) -> str:
        return self.container.find_element(*self._ride_pick_up).text

    @property
    def ride_details_button(self) -> WebElement:
        return self.container.find_element(*self._ride_overview_button)

    @property
    def ride_id(self) -> str:
        """Return the unique ride ID for a ride row.

        The get_attribute method is used instead of .text as the ride_id is a hidden attribute.
        """
        return self.container.find_element(
            *self._ride_id).get_attribute('innerText')

    @property
    def status(self) -> str:
        return self.container.find_element(*self._ride_status).text

    def open_ride_overview(self) -> object:
        """Navigate to the ride overview modal for a ride."""
        self.ride_details_button.click()

        return RideOverviewModal(self).wait_for_component_to_be_present()
Ejemplo n.º 15
0
class ScheduledRidesList(Component):
    """Objects and methods for the Scheduled Ride list."""

    ROOT_LOCATOR: Selector = Selectors.data_id('scheduled-rides-container')

    @property
    def scheduled_rides(self) -> Tuple[ScheduledRideRow, ...]:
        return tuple(
            ScheduledRideRow(self, ele)
            for ele in self.container.find_elements(
                *ScheduledRideRow.ROOT_LOCATOR))

    def cancel_scheduled_ride(self) -> None:
        """Cancel the first ride in the scheduled rides container.

        This method will always cancel the first ride as it is the only ride that can be cancelled
        in the entire series. All other rides will feature a 'Delete' button as they will be
        of status 'Scheduled' instead of 'Booked'.
        """
        self.scheduled_rides[0].cancel_button.click()

    def delete_scheduled_ride(self, date: str) -> None:
        """Delete a scheduled ride.

        :param date: A specified date in MM-DD-YYYY format.
        """
        ride_row: ScheduledRideRow = self.filter_scheduled_rides(date)
        ride_row.delete_ride()

    def filter_scheduled_rides(self, date: str) -> ScheduledRideRow:
        """Filter all scheduled rides for a match with a specific date.

        :param date: A specified date in MM-DD-YYYY format.
        """
        ride_list: Tuple[ScheduledRideRow,
                         ...] = tuple(ride for ride in self.scheduled_rides
                                      if ride.date == date)

        if not ride_list:
            raise NoSuchElementException(
                f'A ride could not be found for the date: {date}.')
        return ride_list[0]
Ejemplo n.º 16
0
class GroupsList(Component):
    """Objects and methods for the Groups List component."""

    ROOT_LOCATOR: Selector = Selectors.data_id('group-list-container')

    @property
    def groups_rows(self) -> Tuple:
        """Return a tuple of groups rows within the list container.
        """
        rows: WebElements = self.container.find_elements(
            *GroupRow.ROOT_LOCATOR)
        return tuple(GroupRow(self, element) for element in rows)

    def filter_rows(self,
                    group: dict,
                    group_name: str = None) -> Union[GroupRow, None]:
        """Filter all group rows for a match with a group name.

        :param group_name: An optional name for a group.
        :param group: A group object yielded from a group fixture.
        """
        name: str = group['name'] if group_name is None else group_name
        group_list: list = [
            row for row in self.groups_rows if name == row.name
        ]

        if not group_list:
            return None
        return group_list[0]

    def surface_group_row(self,
                          group: dict,
                          group_name: str = None) -> Union[GroupRow, None]:
        """Raise a group row that matches a group name.

        :param group_name: An optional name for a group.
        :param group: A group object yielded from a group fixture.
        """
        return self.filter_rows(group, group_name=group_name)
Ejemplo n.º 17
0
class AutocompleteSuggestions(object):
    """Objects and methods for autocomplete suggestions.

    :example usage:
        suggestions = AutocompleteSuggestions(self)
        suggestions.select_suggestion(location='4506 Emperor Boulevard Durham, NC, USA')
    """
    def __init__(self, page):
        self.driver = page.driver

    _places_autocomplete_suggestion: Selector = Selectors.data_id(
        'places-autocomplete-suggestion')

    @property
    def suggestion_list(self) -> WebElements:
        return self.driver.find_elements(*self._places_autocomplete_suggestion)

    def filter_suggestions(self, location: str) -> WebElement:
        """Filter location suggestions for a match with a location.

        :param location: The location for a ride.
        """
        locations: Tuple[WebElement, ...] = tuple(
            loc for loc in self.suggestion_list
            if location in loc.text.replace('\n', ' '))

        if not location:
            raise NoSuchElementException(
                f'A suggestion for: {location} could not be located.')
        return locations[0]

    def select_suggestion(self, location: str) -> None:
        """Select a location suggestion.

        :param location: The location for a ride.
        """
        self.filter_suggestions(location).click()
        self.driver.wait_until_not_present(
            *self._places_autocomplete_suggestion, wait_time=1)
Ejemplo n.º 18
0
class Services(Base):
    """Services Page objects and methods for the OnDemand Admin application."""

    URL_PATH: str = f'{Base.URL_PATH}/services'
    ROOT_LOCATOR: Selector = Selectors.data_id('services-page-container')

    @property
    def service_card_list(self) -> ServiceCardList:
        return ServiceCardList(self)

    def navigate_to_edit_by_service_id(self, service: dict) -> object:
        """Navigate to the Details Page for a specific service using a direct URL.

        This is a useful method for bypassing the application work flows following creation of a
        service using the API.

        :param service: The service yielded from a service fixture.
        """
        service_id: int = service['service_id']
        service_details_url: str = f'{self.URL_PATH}/{service_id}/details'
        self.driver.get(service_details_url)

        return EditService(self.driver, service_details_url).wait_for_page_to_load()

    def navigate_to_locations_by_service(self, service: dict) -> object:
        """Navigate to the Locations page for a specific service using a direct URL.

        This is a useful method for bypassing the application work flows following creation of a
        service using the API.

        :param service: The service intended for navigation.
        """
        service_id: int = service['service_id']
        locations_url = f'{self.URL_PATH}/{service_id}/locations'
        self.driver.get(locations_url)

        return Locations(self)
class PickupTimeModal(Component):
    """Objects and methods for the Pickup Time Modal component."""

    ROOT_LOCATOR: Selector = Selectors.data_id('pickup-time-modal-container')
    _asap_ride_radio: Selector = Selectors.value('use-now')
    _future_ride_radio: Selector = Selectors.value('use-later')
    _back_button: Selector = Selectors.data_id('pickup-time-modal-back-button')
    _continue_button: Selector = Selectors.data_id(
        'pickup-time-modal-continue-button')
    _date_field: Selector = Selectors.data_id('pickup-time-modal-date-field')
    _time_field: Selector = Selectors.data_id('pickup-time-modal-time-field')

    @property
    def asap_ride_button(self) -> WebElement:
        return self.container.find_element(*self._asap_ride_radio)

    @property
    def back_button(self) -> WebElement:
        return self.container.find_element(*self._back_button)

    @property
    def continue_button(self) -> WebElement:
        return self.container.find_element(*self._continue_button)

    @property
    def date_field(self) -> WebElement:
        return self.container.find_element(*self._date_field)

    @property
    def future_ride_button(self) -> WebElement:
        return self.container.find_element(*self._future_ride_radio)

    @property
    def time_field(self) -> WebElement:
        return self.container.find_element(*self._time_field)

    def submit_pickup_time(self) -> None:
        """Submit a selected pick up time."""
        self.continue_button.click()
class RideSubscriptionTable(Component):
    """Objects and methods for the Ride Subscription Table component.

    The Ride Subscription Table component may be found by selecting 'Active', 'Upcoming' or
    'Complete' under 'Recurring Rides' in the Rides page side bar navigation panel. It is only
    located within these spaces and should not be found elsewhere at this time.
    """

    ROOT_LOCATOR: Selector = Selectors.data_id('subscription-table-container')

    @property
    def subscription_rows(self) -> Tuple[SubscriptionRow, ...]:
        return tuple(
            SubscriptionRow(self, element)
            for element in self.container.find_elements(*SubscriptionRow.ROOT_LOCATOR)
        )

    def filter_subscription_rows(self, subscription_id: str) -> Union[SubscriptionRow, None]:
        """Filter all subscription rows for a match with a subscription ID.

        :param subscription_id: A unique subscription ID.
        """
        row_list: Tuple[SubscriptionRow, ...] = tuple(
            row for row in self.subscription_rows if row.id == subscription_id
        )

        if not row_list:
            return None
        return row_list[0]

    def surface_subscription_row(self, subscription_id: str) -> Union[SubscriptionRow, None]:
        """Surface a subscription row.

        :param subscription_id: A unique subscription ID.
        """
        return self.filter_subscription_rows(str(subscription_id))
Ejemplo n.º 21
0
class AddressForm(Component):
    """Objects and methods for the Address Form component."""

    ROOT_LOCATOR: Selector = Selectors.data_id('address-container')
    _address_field: Selector = Selectors.name('address')
    _cancel_button: Selector = Selectors.data_id('cancel-button')
    _delete_button: Selector = Selectors.data_id('delete-address-button')
    _label_field: Selector = Selectors.name('name')
    _save_button: Selector = Selectors.data_id('save-address-button')

    @property
    def address_field(self) -> WebElement:
        return self.container.find_element(*self._address_field)

    @property
    def cancel_button(self) -> WebElement:
        return self.container.find_element(*self._cancel_button)

    @property
    def delete_address_button(self) -> WebElement:
        return self.container.find_element(*self._delete_button)

    @property
    def label_field(self) -> WebElement:
        return self.container.find_element(*self._label_field)

    @property
    def save_button(self) -> WebElement:
        return self.container.find_element(*self._save_button)

    @property
    def suggestions(self) -> AutocompleteSuggestions:
        return AutocompleteSuggestions(self)

    def fill_form(self, address: str, label: str) -> None:
        """Fill out an address form, then submit the form.

        :param address: The street address.
        :param label: The label for the address.
        """
        self.label_field.fill(label)
        self.address_field.fill(address)
        self.suggestions.select_suggestion(location=address)
Ejemplo n.º 22
0
class GroupRow(Component):
    """Objects and methods for the Restricted Groups row."""

    ROOT_LOCATOR = Selectors.data_id('group-row')
    _groups_manage_row_name: Selector = Selectors.data_id('group-name')
    _groups_manage_row_allow_checkbox: Selector = Selectors.data_id(
        'allow-group-checkbox')
    _groups_manage_row_deny_checkbox: Selector = Selectors.data_id(
        'deny-group-checkbox')
    _groups_manage_status: Selector = Selectors.data_id('group-status')
    _group_id: Selector = Selectors.data_id('group-id')

    @property
    def group_id(self) -> str:
        """Return the unique service ID for a service row.

        The get_attribute method is used instead of .text as the service_id is a hidden
        attribute.
        """
        return self.container.find_element(
            *self._group_id).get_attribute('innerText')

    @property
    def name(self) -> str:
        return self.container.find_element(*self._groups_manage_row_name).text

    @property
    def allow_checkbox(self) -> WebElement:
        return self.container.find_element(
            *self._groups_manage_row_allow_checkbox)

    @property
    def deny_checkbox(self) -> WebElement:
        return self.container.find_element(
            *self._groups_manage_row_deny_checkbox)

    @property
    def status(self) -> str:
        return self.container.find_element(*self._groups_manage_status).text
class LegacyRideBooking(Base):
    """Objects and methods for the Legacy Ride Booking page.

    The legacy ride booking page is a work flow which must be supported due to the volume of
    dispatchers who continue to utilize the page over the Admin Dispatch page.
    """

    URL_PATH: str = f'{Base.URL_PATH}/rides/new'

    _asap_ride_radio: Selector = Selectors.data_id('use-now')
    _driver_note: Selector = Selectors.name('note')
    _drop_off_field: Selector = Selectors.name('dropoff')
    _first_name_field: Selector = Selectors.name('first_name')
    _future_ride_radio: Selector = Selectors.data_id('use-later')
    _last_name_field: Selector = Selectors.name('last_name')
    _pay_on_vehicle_radio: Selector = Selectors.radio('cash')
    _phone_field: Selector = Selectors.name('phone')
    _pick_up_field: Selector = Selectors.name('pickup')
    _places_autocomplete_suggestion: Selector = Selectors.data_id(
        'places-autocomplete-suggestion')
    _service_dropdown: Selector = Selectors.name('serviceId')
    _submit_ride_button: Selector = Selectors.data_id('book_ride')
    _total_passenger_field: Selector = Selectors.name('capacity')
    _waive_fee_radio: Selector = Selectors.radio('waive')
    _wheelchair_switch: Selector = Selectors.name('wheelchair')

    @property
    def asap_ride_button(self) -> WebElement:
        return self.driver.find_element(*self._asap_ride_radio)

    @property
    def driver_note_field(self) -> WebElement:
        return self.driver.find_element(*self._driver_note)

    @property
    def drop_off_field(self) -> WebElement:
        return self.driver.find_element(*self._drop_off_field)

    @property
    def first_name_field(self) -> WebElement:
        return self.driver.find_element(*self._first_name_field)

    @property
    def future_ride_button(self) -> WebElement:
        return self.driver.find_element(*self._future_ride_radio)

    @property
    def last_name_field(self) -> WebElement:
        return self.driver.find_element(*self._last_name_field)

    @property
    def pay_on_vehicle_button(self) -> WebElement:
        return self.driver.find_element(*self._pay_on_vehicle_radio)

    @property
    def phone_field(self) -> WebElement:
        return self.driver.find_element(*self._phone_field)

    @property
    def pick_up_field(self) -> WebElement:
        return self.driver.find_element(*self._pick_up_field)

    @property
    def service_dropdown(self) -> Select:
        return self.driver.find_dropdown(*self._service_dropdown)

    @property
    def submit_ride_button(self) -> WebElement:
        return self.driver.find_element(*self._submit_ride_button)

    @property
    def suggestions(self) -> AutocompleteSuggestions:
        return AutocompleteSuggestions(self)

    @property
    def total_passenger_field(self) -> WebElement:
        return self.driver.find_element(*self._total_passenger_field)

    @property
    def waive_fee_button(self) -> WebElement:
        return self.driver.find_element(*self._waive_fee_radio)

    @property
    def wheelchair_switch(self) -> WebElement:
        return self.driver.find_element(*self._wheelchair_switch)

    def fill_ride_form(self, service: dict, ride: dict) -> None:
        """Fill out the legacy ride form.

        Use send_keys for phone_field as clearing the field within the fill method causes input
        to occur at the end of the field. This results in an input failure.

        :param service: The service yielded from a service API fixture.
        :param ride: The ride intended for booking.
        """
        self.first_name_field.fill(ride['rider']['first_name'])
        self.last_name_field.fill(ride['rider']['last_name'])
        self.last_name_field.fill(Keys.TAB)
        self.phone_field.send_keys(ride['rider']['phone'])

        self.select_pick_up_location(ride['pickup']['address'])
        self.select_drop_off_location(ride['dropoff']['address'])

        #
        # Occurs further down the stack than its form location as it intermittently failed when it
        # was called first.
        #
        self.select_a_service(service['service_id'])

        if ride['note'] is not None:
            self.driver_note_field.send_keys(ride['note'])

    def pay_ride_fee(self, method: str) -> None:
        """Select a payment method based on a method string.

        :param method: A payment method for the ride.
        """
        if method == 'cash':
            self.pay_on_vehicle_button.scroll_to().click()
        elif method == 'waive':
            self.waive_fee_button.scroll_to().click()
        else:
            raise PaymentMethodException(
                f'The payment method: "{method}" is not allowed for this ride.',
            )

    def select_a_service(self, service_id: int) -> None:
        """Select a service within the service drop down.

        :param service_id: The service ID yielded from a service API fixture.
        """
        try:
            self.driver.wait_until_visible(*self._service_dropdown,
                                           wait_time=4)
            self.service_dropdown.select_by_value(service_id)
        except NoSuchElementException:
            raise NoSuchElementException(
                f'The service ID: {service_id} cannot be found within the selected agency.\n'
                f'Please select a valid service within the selected agency.', )

    def select_drop_off_location(self, drop_off: str) -> None:
        """Fill out a drop off location, then select an autocomplete suggestion.

        :param drop_off: The drop off location for a ride.
        """
        self.drop_off_field.fill(drop_off)
        self.suggestions.select_suggestion(drop_off)

    def select_pick_up_location(self, pick_up: str) -> None:
        """Fill out a pick up location, then select an autocomplete suggestion.

        :param pick_up: The pick up location for a ride.
        """
        self.pick_up_field.fill(pick_up)
        self.suggestions.select_suggestion(pick_up)

    def submit_ride_form(self) -> object:
        """Submit a legacy ride form."""
        self.submit_ride_button.scroll_to().click()

        return Details(self.driver).wait_for_page_to_load()
Ejemplo n.º 24
0
class Registration(Page):
    """Objects and methods for the Register page."""

    URL_PATH: str = build_login_url(path='/register')
    ROOT_LOCATOR: Selector = Selectors.class_name('register-container')

    _email_field: Selector = Selectors.name('email')
    _first_name_field: Selector = Selectors.name('first_name')
    _inline_errors: Selector = Selectors.class_name('errors')
    _last_name_field: Selector = Selectors.name('last_name')
    _password_field: Selector = Selectors.name('password')
    _phone_field: Selector = Selectors.name('phone')
    _privacy_policy_field: Selector = Selectors.name('privacy_policy_accepted')
    _repeat_password_field: Selector = Selectors.name('repeat_password')
    _submit_button: Selector = Selectors.data_id('submit-button')
    _username_field: Selector = Selectors.name('username')
    _user_exists_error: Selector = Selectors.data_id('error-message')

    @property
    def email_field(self) -> WebElement:
        return self.driver.find_element(*self._email_field)

    @property
    def first_name_field(self) -> WebElement:
        return self.driver.find_element(*self._first_name_field)

    @property
    def inline_errors(self) -> WebElements:
        return self.driver.find_elements(*self._inline_errors)

    @property
    def inline_error_messages(self) -> List[str]:
        return [error.text for error in self.inline_errors]

    @property
    def last_name_field(self) -> WebElement:
        return self.driver.find_element(*self._last_name_field)

    @property
    def password_field(self) -> WebElement:
        return self.driver.find_element(*self._password_field)

    @property
    def phone_field(self) -> WebElement:
        return self.driver.find_element(*self._phone_field)

    @property
    def privacy_policy_checkbox(self) -> WebElement:
        return self.driver.find_element(*self._privacy_policy_field)

    @property
    def repeat_password_field(self) -> WebElement:
        return self.driver.find_element(*self._repeat_password_field)

    @property
    def submit_button(self) -> WebElement:
        return self.driver.find_element(*self._submit_button)

    @property
    def username_field(self) -> WebElement:
        return self.driver.find_element(*self._username_field)

    @property
    def user_exists_error(self) -> WebElement:
        return self.driver.find_element(*self._user_exists_error)

    def fill_registration_form(self, user: User) -> None:
        """Fill out a user registration form.

        :param user: A user generated from a User Factory.
        """
        self.username_field.fill(user.username)
        self.email_field.fill(user.email)
        self.first_name_field.fill(user.first_name)
        self.last_name_field.fill(user.last_name)
        self.phone_field.fill(user.phone)
        self.password_field.fill(user.password)
        self.repeat_password_field.fill(user.repeat_password)
        self.privacy_policy_checkbox.click()
Ejemplo n.º 25
0
 def __init__(self, page: object, selector: str, tabs: list):
     self.tabs = tabs
     self.ROOT_LOCATOR: Selector = Selectors.data_id(selector)
     super().__init__(page)
Ejemplo n.º 26
0
class RideForm(Component):
    """Objects and methods for the Ride form."""

    ROOT_LOCATOR: Selector = Selectors.data_id('ride-details-container')
    _driver_note_field: Selector = Selectors.name('note')
    _drop_off_field: Selector = Selectors.name('dropoff')
    _pick_up_field: Selector = Selectors.name('pickup')
    _service_drop_down: Selector = Selectors.name('serviceId')
    _service_drop_down_error: Selector = Selectors.data_id(
        'service-dropdown-with-errors')
    _total_passenger_field: Selector = Selectors.name('capacity')
    _wheelchair_check_box: Selector = Selectors.name('wheelchair')

    @property
    def driver_note_field(self) -> WebElement:
        return self.container.find_element(*self._driver_note_field)

    @property
    def drop_off_field(self) -> WebElement:
        return self.container.find_element(*self._drop_off_field)

    @property
    def pick_up_field(self) -> WebElement:
        return self.container.find_element(*self._pick_up_field)

    @property
    def service_drop_down(self) -> Select:
        return self.container.find_dropdown(*self._service_drop_down)

    @property
    def suggestions(self) -> AutocompleteSuggestions:
        return AutocompleteSuggestions(self)

    @property
    def total_passenger_field(self) -> WebElement:
        return self.container.find_element(*self._total_passenger_field)

    @property
    def wheelchair_check_box(self) -> WebElement:
        return self.container.find_element(*self._wheelchair_check_box)

    def fill_ride_info_form(self, service: dict, ride: dict) -> None:
        """Fill out the ride information form.

        The ride param may be of type RecurringRide or Ride depending on the test which is being
        ran. The default type will be Ride as it is the most common data type for testing.

        :param service: The service yielded from a service API fixture.
        :param ride: A ride yielded from a ride fixture.
        """
        try:
            ride_data = ride['ride']
        except KeyError:
            ride_data = ride

        dropoff: dict = ride_data['dropoff']
        note: str = ride_data['note']
        pickup: dict = ride_data['pickup']

        self.select_a_service(service['service_id'])
        self.select_pick_up_location(pickup['address'])
        self.select_drop_off_location(dropoff['address'])

        if note is not None:
            self.driver_note_field.send_keys(note)

    def select_a_service(self, service_id: str) -> None:
        """Select a service within the service drop down.

        :param service_id: The service ID yielded from a service API fixture.
        """
        try:
            self.service_drop_down.select_by_value(service_id)
        except NoSuchElementException:
            raise NoSuchElementException(
                f'The service ID: {service_id} cannot be found within the selected agency.\n'
                f'Please select a valid service within the selected agency.', )

    def select_drop_off_location(self, drop_off: str) -> None:
        """Fill out a drop off location, then select an autocomplete suggestion.

        :param drop_off: The drop off location for a ride.
        """
        self.drop_off_field.fill(drop_off)
        self.suggestions.select_suggestion(drop_off)

    def select_pick_up_location(self, pick_up: str) -> None:
        """Fill out a pick up location, then select an autocomplete suggestion.

        :param pick_up: The pick up location for a ride.
        """
        self.pick_up_field.fill(pick_up)
        self.suggestions.select_suggestion(pick_up)

    def service_error_check(self) -> bool:
        return self.container.find_element(
            *self._service_drop_down_error).is_displayed()
Ejemplo n.º 27
0
class Login(Page):
    """Login methods and objects for all TransLoc applications."""

    URL_PATH: str = build_login_url(path='/login')
    ROOT_LOCATOR: Selector = Selectors.class_name('login-container')

    _error_message: Selector = Selectors.data_id('error-message')
    _forgot_password_link: Selector = Selectors.data_id('forgot-password-link')
    _password_field: Selector = Selectors.name('password')
    _sign_up_button: Selector = Selectors.data_id('sign-up-button')
    _submit_button: Selector = Selectors.data_id('submit-login-button')
    _success_message: Selector = Selectors.data_id('success-message')
    _university_login_button: Selector = Selectors.data_id(
        'university-login-button')
    _username_field: Selector = Selectors.name('username')

    @property
    def error_message(self) -> WebElement:
        return self.driver.find_element(*self._error_message)

    @property
    def forgot_password_link(self) -> WebElement:
        return self.driver.find_element(*self._forgot_password_link)

    @property
    def password_field(self) -> WebElement:
        return self.driver.find_element(*self._password_field)

    @property
    def sign_up_button(self) -> WebElement:
        return self.driver.find_element(*self._sign_up_button)

    @property
    def submit_button(self) -> WebElement:
        return self.driver.find_element(*self._submit_button)

    @property
    def success_message(self) -> WebElement:
        return self.driver.find_element(*self._success_message)

    @property
    def university_login_button(self) -> WebElement:
        return self.driver.find_element(*self._university_login_button)

    @property
    def username_field(self) -> WebElement:
        return self.driver.find_element(*self._username_field)

    def add_auth_token(self):
        """Add a captured authorization token as an environment variable."""
        auth_token = json.loads(os.getenv('AUTH_TOKEN'))
        self.driver.add_cookie(auth_token)

    def capture_token(self) -> None:
        """Capture an authorization token."""
        auth_cookie = self.driver.get_cookies()[1]
        if 'expiry' in auth_cookie:
            del auth_cookie['expiry']

        json_cookie = json.dumps(auth_cookie)

        os.environ['AUTH_TOKEN'] = json_cookie

    def check_login_fail(self) -> None:
        """Check whether an error message is shown."""
        self.driver.wait_until_visible(*self._error_message)

    def login(self, username: Optional[str], password: Optional[str]) -> None:
        """Login in to a TransLoc application.

        :param password: The password for login.
        :param username: The username for login.
        """
        self.username_field.fill(username)
        self.password_field.fill(password)

        self.submit_button.click()
Ejemplo n.º 28
0
class RideCard(Component):
    """Objects and methods for an individual Ride Card."""

    ROOT_LOCATOR: Selector = Selectors.data_id('ride-card')
    card_locator: Selector = Selectors.data_id('ride-card')
    _drop_off_prioritized: Selector = Selectors.data_id('dropoff-prioritized')
    _entire_ride_prioritized: Selector = Selectors.data_id(
        'entire-ride-prioritized')
    _kebab_menu: Selector = Selectors.data_id('ride-card-kebab-menu-button')
    _pick_up_prioritized: Selector = Selectors.data_id('pickup-prioritized')
    _ride_drop_off: Selector = Selectors.data_id('ride-card-drop-off-address')
    _ride_drop_off_time: Selector = Selectors.data_id(
        'ride-card-drop-off-time')
    _ride_email: Selector = Selectors.data_id('ride-card-email')
    _ride_id: Selector = Selectors.data_id('ride-card-ride-id')
    _ride_note: Selector = Selectors.data_id('ride-card-driver-note')
    _ride_pick_up: Selector = Selectors.data_id('ride-card-pick-up-address')
    _ride_pick_up_time: Selector = Selectors.data_id('ride-card-pick-up-time')
    _ride_status: Selector = Selectors.data_id('ride-card-ride-status')
    _ride_wait: Selector = Selectors.data_id('ride-card-ride-wait')
    _rider_name: Selector = Selectors.data_id('ride-card-rider-name')
    _rider_phone: Selector = Selectors.data_id('ride-card-phone')

    @property
    def kebab_menu(self) -> KebabMenu:
        return KebabMenu(self)

    @property
    def prioritization_modal(self) -> RidePrioritizationModal:
        return RidePrioritizationModal(self)

    @property
    def rider_name(self) -> str:
        return self.container.find_element(*self._rider_name).text

    @property
    def drop_off_address(self) -> str:
        return self.container.find_element(*self._ride_drop_off).text

    @property
    def drop_off_time(self) -> str:
        return self.container.find_element(*self._ride_drop_off_time).text

    @property
    def email(self) -> str:
        """Return the rider email for a ride card.

        The get_attribute method is used instead of .text as the email is a hidden attribute.
        """
        return self.container.find_element(
            *self._ride_email).get_attribute('innerText')

    @property
    def note(self) -> str:
        return self.container.find_element(*self._ride_note).text

    @property
    def phone(self) -> str:
        return self.container.find_element(*self._rider_phone).text

    @property
    def pick_up_address(self) -> str:
        return self.container.find_element(*self._ride_pick_up).text

    @property
    def pick_up_time(self) -> str:
        return self.container.find_element(*self._ride_pick_up_time).text

    @property
    def ride_status(self) -> str:
        return self.container.find_element(*self._ride_status).text

    @property
    def ride_wait(self) -> str:
        return self.container.find_element(*self._ride_wait).text

    @property
    def ride_card_kebab_button(self) -> WebElement:
        return self.container.find_element(*self._kebab_menu)

    @property
    def ride_id(self) -> str:
        """Return the unique ride ID for a ride card.

        The get_attribute method is used instead of .text as the ride_id is a hidden attribute.
        """
        return self.container.find_element(
            *self._ride_id).get_attribute('innerText')

    def drop_off_prioritized(self) -> WebElement:
        return self.container.wait_until_present(*self._drop_off_prioritized)

    def entire_ride_prioritized(self) -> WebElement:
        return self.container.wait_until_present(
            *self._entire_ride_prioritized)

    def pick_up_prioritized(self) -> WebElement:
        return self.container.wait_until_present(*self._pick_up_prioritized)

    def open_kebab_menu(self) -> object:
        """Find and open a ride card kebab menu.

        :returns KebabMenu: An instance of the ride's Kebab Menu.
        """
        self.ride_card_kebab_button.click()

        return KebabMenu(self).wait_for_component_to_be_present()
Ejemplo n.º 29
0
class Dispatch(Base):
    """Dispatch Page objects and methods for the OnDemand Admin application."""

    URL_PATH: str = f'{Base.URL_PATH}/dispatch'
    ROOT_LOCATOR: Selector = Selectors.data_id('dispatch-page-container')

    @property
    def cancellation_modal(self) -> CancellationModal:
        return CancellationModal(self)

    @property
    def ride_booking_panel(self) -> RideBookingPanel:
        return RideBookingPanel(self)

    @property
    def ride_card_panel(self) -> RideCardPanel:
        return RideCardPanel(self)

    @property
    def ride_filters(self) -> RideFilters:
        return RideFilters(self)

    @property
    def rider_search(self) -> RiderSearch:
        return RiderSearch(self)

    @property
    def ride_subscription_modal(self) -> RideSubscriptionModal:
        return RideSubscriptionModal(self)

    def cancel_ride(self, cancel_reason: str, ride: dict) -> None:
        """Cancel a ride that matches a ride object.

        :param cancel_reason: The required cancellation reason.
        :param ride: The ride yielded from a ride fixture.
        """
        card: RideCard = self.ride_card_panel.surface_ride_card(ride)
        card.open_kebab_menu()
        card.kebab_menu.cancel_ride_button.click()

        self.cancellation_modal.cancel_ride(cancel_reason)

    def fill_future_ride_form(self, ride: dict) -> None:
        """Fill out the Future Ride form.

        :param ride: The ride yielded from a ride fixture.
        """
        self.ride_booking_panel.future_ride_form.fill_future_ride_form(ride)

    def fill_recurring_ride_form(self, ride: dict) -> None:
        """Fill out the Recurring Ride form.

        :param ride: The ride yielded from a recurring ride fixture.
        """
        self.ride_booking_panel.recurring_ride_form.fill_recurring_ride_form(
            ride)

    def fill_ride_form(self, service: dict, ride: dict) -> None:
        """Fill out the Dispatch page ride form.

        :param service: The service yielded from a service API fixture.
        :param ride: The ride yielded from a ride fixture.
        """
        self.ride_booking_panel.open_book_a_ride_form()
        self.ride_booking_panel.passenger_form.fill_passenger_info_form(ride)
        self.ride_booking_panel.ride_form.fill_ride_info_form(service, ride)
Ejemplo n.º 30
0
class GroupUserList(Component):
    """Objects and methods for the Groups List component."""

    ROOT_LOCATOR: Selector = Selectors.data_id('rider-list-container')

    @property
    def user_rows(self) -> Tuple:
        """Return a tuple of rider rows within the list container."""
        rows: WebElements = self.container.find_elements(
            *GroupUserRow.ROOT_LOCATOR)
        return tuple(GroupUserRow(self, element) for element in rows)

    def filter_rows(
        self,
        user: dict,
        user_email: str = None,
        wait_for_row: bool = False,
    ) -> Union[GroupUserRow, None]:
        """Filter all user rows for a match with a user name.

        :param user_email: An optional email for a user.
        :param user: A user object yielded from a user fixture.
        :param wait_for_row: Optionally wait for a row.
        """
        email: str = user['email'] if user_email is None else user_email

        if wait_for_row:
            self.wait_for_user_row(user=user, user_email=user_email)

        group_list: list = [
            row for row in self.user_rows if email == row.email
        ]

        if not group_list:
            return None
        return group_list[0]

    def wait_for_user_row(self, user: dict, user_email: str = None) -> None:
        """Wait until a user row is located which contains a specific email.

        An increased wait time is used within this function as rows can take upward of five seconds
        to appear within the DOM. This amount is nearly doubled when running in parallel.

        :param user_email: An optional email for a user.
        :param user: A user object yielded from a user fixture.
        """
        email: str = user['email'] if user_email is None else user_email
        self.driver.wait_until_visible(*Selectors.text(email), wait_time=10)

    def surface_user_row(
        self,
        user: dict,
        user_email: str = None,
        wait_for_row: bool = False,
    ) -> Union[GroupUserRow, None]:
        """Raise a user row that matches a user name.

        :param user_email: An optional email for a user.
        :param user: A user object yielded from a user fixture.
        :param wait_for_row: Optionally wait for a row.
        """
        return self.filter_rows(user=user,
                                user_email=user_email,
                                wait_for_row=wait_for_row)