Skip to content
144 changes: 79 additions & 65 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,111 +13,125 @@ Python [unittest](https://docs.python.org/2/library/unittest.html) test cases.

$ pip install robotframework-pageobjects

## How it Works

## Demo

Check out and run the [demo](https://github.com/ncbi/robotframework-pageobjects/tree/master/demo).

## How the demo works

Here's a Robot test case using some page objects written using the `Page` base class. We need to import any page objects libraries we need in our test
case. **Note**: The `Page` class inherits from Selenium2Library, so all methods (keywords) in Selenium2Library are available in your tests, and from `self` from within one of your page objects.

*test_google.robot*:
*test_pubmed.txt*:

*** Settings ***

Documentation Tests searching Google and ending up on Apple.
Documentation My first IFT tests
...
Library google.Page
Library google.ResultPage

Library pubmed.PubmedHomePage
Library pubmed.PubmedDocsumPage
Library pubmed.PubmedArticlePage

*** Test Cases ***
When a user searches Pubmed for a term, the first result page's body should contain the search term
Open Pubmed
Search For cat
Click Result On Pubmed Docsum Page 1
Pubmed Article Page Body Should Contain cat
[Teardown] Close Pubmed Article Page

Test Google To Apple
Open Google
Search Google For Apple Computers
On Google Result Page Click Result 1
Title Should Be Apple
[Teardown] Close Google

This shows you can write the same test, using the same page object libraries outside of Robot, using, for example, Python's unittest module:
This shows you can write the same test, using the same page object libraries outside of Robot, using, for example, Python's unittest module. *test_google.py*:

from pubmed import PubmedHomePage
import unittest
import google


class TestGoogleSearch(unittest.TestCase):
class PubmedTestCase(unittest.TestCase):

def setUp(self):
self.google_page = google.Page().open()
self.pubmed_homepage = PubmedHomePage()
self.pubmed_homepage.open()

def test_google_search_to_apple(self):
result_page = self.google_page.search("apple computers")
result_page.click_result(1)
result_page.title_should_be("Apple")
def test_first_result_page_body_should_contain_search_term(self):
pubmed_docsum_page = self.pubmed_homepage.search_for("cat")
self.article_page = pubmed_docsum_page.click_result(1)
self.article_page.body_should_contain("cat")

def tearDown(self):
self.google_page.close()

unittest.main()
self.article_page.close()

if __name__ == "__main__":
unittest.main()

Now we need an actual Google Robot library to make the test work:
Now we need an actual pubmed page objects to make the test work:

*google.py*:
*pubmed.py*:

from robotpageobjects import Page, robot_alias
from robot.utils import asserts


class GooglePage(Page):

"""
Base Google Page

"""
uri = "/"
class PubmedHomePage(Page):
""" Models the Pubmed home page at:
HOST://ncbi.nlm.nih.gov/pubmed"""

# name attribute tells Robot Keywords what name to put
# after the defined method. So, def foo.. aliases to "Foo Google".
# If no name is defined, the name will be the name of the page object
# class, in this case `Page`.
name = "Google"
name = "Pubmed"
uri = "/pubmed"

# selectors dictionary is an inheritable dictionary
# mapping names to Selenium2Library locators.
selectors = {
"search input": "xpath=//input[@name='q']",
"search button: "id=gbqfba",
"search input": "id=term",
"search button": "id=search",
}

def search(self, term):
self.input_text("search input", term)
self.click_element("search button")
return ResultPage()

**Note**: You must return *something* from public (non-underscored) page object methods: either a value from a getter method or a page object instance from non-getter methods. Remember, when you navigate to a new page by clicking a link, submitting a form etc. you should return the appropriate page object.
@robot_alias("type_in__name__search_box")
def type_in_search_box(self, txt):
self.input_text("search input", txt)
return self

Now we want to code a Google search result page. Here's the Google Result page object:
@robot_alias("click__name__search_button")
def click_search_button(self):
self.click_button("search button")
return PubmedDocsumPage()

*google.py*:
@robot_alias("search__name__for")
def search_for(self, term):
self.type_in_search_box(term)
return self.click_search_button()

...
class ResultPage(Page):

"""
A Google Result page. Inherits from Google Page.
"""
# Google uses ajax requests for their searches.
uri = "/#q=cat"
class PubmedDocsumPage(Page):
"""Models a Pubmed search result page. For example:
http://www.ncbi.nlm.nih.gov/pubmed?term=cat """

uri = "/pubmed/?term={term}"

name = "Google Result Page"
selectors = {
"nth result link": "xpath=(//div[@class='rslt'])[{n}]/p/a",
}

@robot_alias("click_result_on__name__")
def click_result(self, i):
# Calling resolve_selector fills in the "n" variable in
# the selector template at-run-time for the "nth selector link".
# We need to cast the 'i' method parameter to an 'int' because Robot passes
# in all keyword parameters as strings.
locator = self.resolve_selector("nth result link", n=int(i))

# Now, we pass the resolved locator to the inherited click_link method.
self.click_link(locator)
return Page()
return PubmedArticlePage()

class PubmedArticlePage(Page):

uri = "/pubmed/{article_id}"

@robot_alias("__name__body_should_contain")
def body_should_contain(self, str, ignore_case=True):
ref_str = str.lower() if ignore_case else str
ref_str = ref_str.encode("utf-8")
body_txt = self.get_text("css=body").encode("utf-8").lower()
asserts.assert_true(ref_str in body_txt, "body text does not contain %s" %ref_str)
return self


**Note**: You must return *something* from public (non-underscored) page object methods: either a value from a getter method or a page object instance from non-getter methods. Remember, when you navigate to a new page by clicking a link, submitting a form etc. you should return the appropriate page object.

The rest of this README explains many more details around writing page objects and putting them to work in tests.

## Setting Options

Expand Down
21 changes: 8 additions & 13 deletions demo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,16 @@ This directory contains a demo of the Robot Page Objects package. It demonstrate
suprememly readable Robot test leveraging Selenium2Library to create page object libraries, which are usable
in Robot and outside of Robot.

How to run the demos
How to run the demo
--------------------

1. Create a virtual environment
2. pip install .
3. activate the virtual environment
4. $ pybot -vbrowser:firefox -vbaseurl:http://www.google.com test_google.robot
1. Create a virtual environment, then
1. pip install robotframeworkpageobjects
2. $ pybot -vbrowser:firefox -vbaseurl:http://www.ncbi.nlm.nih.gov test_pubmed.txt

To run the Python unittest example:
$ export PO_BASURL=http://www.google.com
$ python test_google.py
$ export PO_BASEURL=http://www.ncbi.nlm.nih.gov
$ export PO_BROWSER=firefox
$ python test_pubmed.py

To run the unittest example in Firefox, set the browser environment variable:
$ export PO_BASURL=http://www.google.com
$ export PO_BROWSERL=firefox
$ python test_google.py

This demo no longer works in Phantomjs. We plan on fixing that in the future.
By default tests will run in PhantomJS unless you specify otherwise.
63 changes: 0 additions & 63 deletions demo/google.py

This file was deleted.

88 changes: 88 additions & 0 deletions demo/pubmed.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
from robotpageobjects import Page, robot_alias
from robot.utils import asserts


class PubmedHomePage(Page):
""" Models the Pubmed home page at:
HOST://ncbi.nlm.nih.gov/pubmed"""


# Allows us to call this page
# something other than the default "Pubmed Home Page"
# at the end of keywords.
name = "Pubmed"

# This page is found at baseurl + "/pubmed"
uri = "/pubmed"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have both "uri" and "uri_template" in this file. I think we made it so that both work, but I would think a demo should be simple and consistent.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catches. Pushed changes as per your comments.


# inheritable dictionary mapping human-readable names
# to Selenium2Library locators. You can then pass in the
# keys to Selenium2Library actions instead of the locator
# strings.
selectors = {
"search input": "id=term",
"search button": "id=search",
}


# Use robot_alias and the "__name__" token to customize
# where to insert the optional page object name
# when calling this keyword. Without the "__name__"
# token this method would map to either "Type In Search Box",
# or "Type In Search Box Pubmed". Using "__name__" we can call
# "Type in Pubmed Search Box foo".
@robot_alias("type_in__name__search_box")
def type_in_search_box(self, txt):
self.input_text("search input", txt)

# We always return something from a page object,
# even if it's the same page object instance we are
# currently on.
return self

@robot_alias("click__name__search_button")
def click_search_button(self):
self.click_button("search button")

# When navigating to another type of page, return
# the appropriate page object.
return PubmedDocsumPage()

@robot_alias("search__name__for")
def search_for(self, term):
self.type_in_search_box(term)
return self.click_search_button()


class PubmedDocsumPage(Page):
"""Models a Pubmed search result page. For example:
http://www.ncbi.nlm.nih.gov/pubmed?term=cat """

uri = "/pubmed/?term={term}"

# This is a "selector template". We are parameterizing the
# nth result in this xpath. We call this from click_result, below.
selectors = {
"nth result link": "xpath=(//div[@class='rslt'])[{n}]/p/a",
}

@robot_alias("click_result_on__name__")
def click_result(self, i):

# For selector templates, we need to resolve the selector to the
# locator first, before finding or acting on the element.
locator = self.resolve_selector("nth result link", n=int(i))
self.click_link(locator)
return PubmedArticlePage()

class PubmedArticlePage(Page):

uri = "/pubmed/{article_id}"

@robot_alias("__name__body_should_contain")
def body_should_contain(self, str, ignore_case=True):
ref_str = str.lower() if ignore_case else str
ref_str = ref_str.encode("utf-8")
body_txt = self.get_text("css=body").encode("utf-8").lower()
asserts.assert_true(ref_str in body_txt, "body text does not contain %s" %ref_str)
return self
20 changes: 0 additions & 20 deletions demo/test_google.py

This file was deleted.

Loading