6. Controller Service

6.1. About

Controller services allow developers to modularize controllers for larger web applications. Services are a lightweight resource that simply consume a given controller, and allow developers to design functionality using the same contextual logic as they would within a controller.

6.2. Best Practices

If a controller is exceeding some 800 lines of code, it may be beneficial for both organization and productivity to compartementalize strings of functionality operating off of the same base component.

6.3. Attributes

When a service is instantiated, the constructor automatically binds the following attributes:

  • controller: Reference to parent controller instance.
  • browser: Reference to parent controller’s webdriver
  • components: Reference to parent controller’s components.
  • env: Reference to parent controller’s env resource.

6.4. Example

The following example is an abstraction of the Google controller featured on Getting Started:

from pyscc import Controller, Component, Service, component_element, \
    component_elements, component_group


class Home(Component):

    _ = 'body > app'  # optional root selector to be applied to all component items

    @component_element
    def search_bar(self):
        # we only need to return the selector
        # pyscc will automatically determine whether it's xpath or css
        return 'input#lst-ib'

    @component_element
    def search_button(self):
        # the element wrapper allows us to format our selectors
        # ref.search_button.fmt(value='Google Search')
        return '//input[@type="submit"][@value="{value}"]'


class Results(Component):

    @component_elements
    def results(self):
        return 'div.g'


class SearchService(Service):

    def search(self, query, redirect=False):
        # navigate to base url
        self.controller.navigate('/')
        # create reference to our home component
        home = self.components.home
        # wait 5 seconds for search bar to be visible
        # write query in the search bar
        home.search_bar\
            .wait_visible(5, error=True)\
            .send_input(query)

        # target the search button with the text "Google Search"
        # click on the search button
        home.search_button\
            .fmt('Google Search')\
            .click()


class Google(Controller):

    def __init__(self, browser, base_url):
        super(Product, self).__init__(browser, base_url, {
            'home': Home,
            'results': Results
        })
        self.add_service('search', SearchService)

    def number_of_results():
        return self.components.results.results.count()

The sample above can be utilized like so:

from selenium import webdriver

google = Google(webdriver.Chrome(), 'https://google.com')
google.services.search.search('py-component-controller')
# ensure at least one result is available within 5 seconds
assert google.components.results.results\
    .wait_for(5, length=1)

# terminate our controller's webdriver
google.exit()