From ea0d4f9710ea48752dc8f0395fda84882aecf4a7 Mon Sep 17 00:00:00 2001 From: cohenaa Date: Tue, 17 Mar 2015 13:14:34 -0400 Subject: [PATCH 01/15] foo --- robotpageobjects/base.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/robotpageobjects/base.py b/robotpageobjects/base.py index 1b2c4fc..66902d1 100755 --- a/robotpageobjects/base.py +++ b/robotpageobjects/base.py @@ -523,6 +523,7 @@ class _BaseActions(_S2LWrapper): """ _abstracted_logger = abstractedlogger.Logger() + ROBOT_LISTENER_API_VERSION = 2 def __init__(self, *args, **kwargs): """ @@ -532,6 +533,7 @@ def __init__(self, *args, **kwargs): #_SelectorsManager.__init__(self, *args, **kwargs) super(_BaseActions, self).__init__(*args, **kwargs) + self.ROBOT_LIBRARY_LISTENER = self self._option_handler = OptionHandler() self._is_robot = Context.in_robot() self.selenium_speed = self._option_handler.get("selenium_speed") or 0 From 9accc910ccf7699d1531c99a5b80f78bfc97d920 Mon Sep 17 00:00:00 2001 From: cohenaa Date: Tue, 17 Mar 2015 16:07:57 -0400 Subject: [PATCH 02/15] Instrucitons on how to test in README --- tests/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/README.md b/tests/README.md index 4d9a8a9..47a70fe 100644 --- a/tests/README.md +++ b/tests/README.md @@ -6,6 +6,7 @@ scenarios directory. In general each functional test is executed both in the Rob unittest context. Functional tests are found in functional.py. They run actual browsers. Unittests are found in unit .py and test simple inputs and outputs of critical page object methods and helpers. +- Install nose with `$ pip install nose`, then run tests: $ nosetests -vs tests/test_unit.py tests/test_functional.py`. - The `scenarios` directory contains the unittests and robot tests which the page object framework tests call in a subprocess. - The `site` directory contains the sample site under test From 7cfa163313d8af0107301e03b9e34eae26fc3808 Mon Sep 17 00:00:00 2001 From: cohenaa Date: Fri, 20 Mar 2015 09:55:52 -0400 Subject: [PATCH 03/15] Added test --- mylistener.py | 16 ++++++++++++++++ mypage.py | 5 +++++ test.robot | 17 +++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 mylistener.py create mode 100644 mypage.py create mode 100644 test.robot diff --git a/mylistener.py b/mylistener.py new file mode 100644 index 0000000..fad1449 --- /dev/null +++ b/mylistener.py @@ -0,0 +1,16 @@ +from robot.libraries.BuiltIn import BuiltIn + + +ROBOT_LISTENER_API_VERSION = 2 +session_id = None + +def end_keyword(name, attrs): + if "Open" in name: + se2 = BuiltIn().get_library_instance('Selenium2Library') + global session_id + session_id = se2._current_browser().session_id + print "Tag running Sauce job, with session_id: %s" % session_id + +def end_test(name, attrs): + global session_id + print "report %s status for session ID: %s" % (name, session_id) diff --git a/mypage.py b/mypage.py new file mode 100644 index 0000000..e45eda9 --- /dev/null +++ b/mypage.py @@ -0,0 +1,5 @@ +from robotpageobjects import Page + + +class MyPage(Page): + uri = "/" diff --git a/test.robot b/test.robot new file mode 100644 index 0000000..b1e9740 --- /dev/null +++ b/test.robot @@ -0,0 +1,17 @@ +*** Settings *** +Documentation Some docs + +Library mypage.MyPage + +*** Test Cases *** + +Test 1 + Open MyPage + [teardown] Close MyPage + +Test 2 + Open MyPage + [teardown] Close MyPage + + + From dc5428380811ec008561460c3ad99ae9ede87989 Mon Sep 17 00:00:00 2001 From: cohenaa Date: Fri, 20 Mar 2015 11:19:45 -0400 Subject: [PATCH 04/15] Library is listener --- robotpageobjects/base.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/robotpageobjects/base.py b/robotpageobjects/base.py index 66902d1..ea0c458 100755 --- a/robotpageobjects/base.py +++ b/robotpageobjects/base.py @@ -525,6 +525,23 @@ class _BaseActions(_S2LWrapper): _abstracted_logger = abstractedlogger.Logger() ROBOT_LISTENER_API_VERSION = 2 + def _start_test(self, name, attrs): + self._current_test = name + + def _end_keyword(self, name, attrs): + """ Called after every keyword is called in Robot test. + We need to get the session ID here, because the browser must + have been open. Also, we only have a session ID when we use + Remote WebDriver (Sauce). + """ + session_id = None + try: + session_id = self.session_id + except AttributeError: + return + + self.log("Tag sauce job %s with %s" %(session_id, self._current_test)) + def __init__(self, *args, **kwargs): """ Initializes the options used by the actions defined in this class. @@ -533,6 +550,9 @@ def __init__(self, *args, **kwargs): #_SelectorsManager.__init__(self, *args, **kwargs) super(_BaseActions, self).__init__(*args, **kwargs) + # Make this library a listener so we can + # centralize robot listener hooks, esp. for + # sauce. self.ROBOT_LIBRARY_LISTENER = self self._option_handler = OptionHandler() self._is_robot = Context.in_robot() From 18e4fb406ba461d7283759389713cf999cd0ddef Mon Sep 17 00:00:00 2001 From: cohenaa Date: Fri, 20 Mar 2015 12:02:32 -0400 Subject: [PATCH 05/15] Added end_test --- robotpageobjects/base.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/robotpageobjects/base.py b/robotpageobjects/base.py index ea0c458..7d9c4db 100755 --- a/robotpageobjects/base.py +++ b/robotpageobjects/base.py @@ -525,9 +525,23 @@ class _BaseActions(_S2LWrapper): _abstracted_logger = abstractedlogger.Logger() ROBOT_LISTENER_API_VERSION = 2 + # For keeping track if we've tagged a sauce + # job. + _session_to_test_hash = {} + + @property + def _sauce_job_registered(self): + return self.session_id in self._session_to_test_hash + + def _register_sauce_job(self): + self._session_to_test_hash[self.session_id] = self._current_test + def _start_test(self, name, attrs): self._current_test = name + def _end_test(self, name, attrs): + self.log("Tag sauce job, %s with %s" %(self.session_id, attrs["status"])) + def _end_keyword(self, name, attrs): """ Called after every keyword is called in Robot test. We need to get the session ID here, because the browser must @@ -540,7 +554,9 @@ def _end_keyword(self, name, attrs): except AttributeError: return - self.log("Tag sauce job %s with %s" %(session_id, self._current_test)) + if not self._sauce_job_registered: + self.log("Tag sauce job %s with %s" %(session_id, self._current_test)) + self._register_sauce_job() def __init__(self, *args, **kwargs): """ From ebd88528d4a68bbf361d33290b8958fe9022b094 Mon Sep 17 00:00:00 2001 From: cohenaa Date: Fri, 20 Mar 2015 12:12:23 -0400 Subject: [PATCH 06/15] Removed mylistener.py --- mylistener.py | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 mylistener.py diff --git a/mylistener.py b/mylistener.py deleted file mode 100644 index fad1449..0000000 --- a/mylistener.py +++ /dev/null @@ -1,16 +0,0 @@ -from robot.libraries.BuiltIn import BuiltIn - - -ROBOT_LISTENER_API_VERSION = 2 -session_id = None - -def end_keyword(name, attrs): - if "Open" in name: - se2 = BuiltIn().get_library_instance('Selenium2Library') - global session_id - session_id = se2._current_browser().session_id - print "Tag running Sauce job, with session_id: %s" % session_id - -def end_test(name, attrs): - global session_id - print "report %s status for session ID: %s" % (name, session_id) From fc69d6e8c4c20a0fc6a2deffabcb3ea730abf8be Mon Sep 17 00:00:00 2001 From: cohenaa Date: Fri, 20 Mar 2015 15:21:25 -0400 Subject: [PATCH 07/15] Got robot tagging tests --- requirements.txt | 1 + robotpageobjects/base.py | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/requirements.txt b/requirements.txt index 0944fb5..c3116db 100755 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,5 @@ decorator mock==1.0.1 requests==2.1.0 robotframework-selenium2library==1.6.0 +python-saucerest uritemplate==0.6 diff --git a/robotpageobjects/base.py b/robotpageobjects/base.py index 7d9c4db..6f06052 100755 --- a/robotpageobjects/base.py +++ b/robotpageobjects/base.py @@ -16,6 +16,7 @@ from . import exceptions from .context import Context from .optionhandler import OptionHandler +import saucelabs.saucerest as saucerest class _Keywords(object): @@ -541,6 +542,9 @@ def _start_test(self, name, attrs): def _end_test(self, name, attrs): self.log("Tag sauce job, %s with %s" %(self.session_id, attrs["status"])) + status = attrs["status"] == "PASS" + self.log(status) + self._saucerest.update_job(self.session_id, dict(passed=status)) def _end_keyword(self, name, attrs): """ Called after every keyword is called in Robot test. @@ -551,11 +555,20 @@ def _end_keyword(self, name, attrs): session_id = None try: session_id = self.session_id + self._saucerest = saucerest.SauceRest( + username=self.sauce_username, + password=self.sauce_apikey + ) + except AttributeError: return if not self._sauce_job_registered: self.log("Tag sauce job %s with %s" %(session_id, self._current_test)) + data = dict( + name = self._current_test + ) + self._saucerest.update_job(session_id, data) self._register_sauce_job() def __init__(self, *args, **kwargs): From 7f6cca905e8bc24494581bac807973e996bfe70b Mon Sep 17 00:00:00 2001 From: cohenaa Date: Fri, 20 Mar 2015 15:29:35 -0400 Subject: [PATCH 08/15] Added failing test so we can see it fail in sauce --- robotpageobjects/base.py | 14 +++++++++----- test.robot | 1 + 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/robotpageobjects/base.py b/robotpageobjects/base.py index 6f06052..733ccdc 100755 --- a/robotpageobjects/base.py +++ b/robotpageobjects/base.py @@ -555,16 +555,20 @@ def _end_keyword(self, name, attrs): session_id = None try: session_id = self.session_id - self._saucerest = saucerest.SauceRest( - username=self.sauce_username, - password=self.sauce_apikey - ) - + except AttributeError: return if not self._sauce_job_registered: self.log("Tag sauce job %s with %s" %(session_id, self._current_test)) + try: + self._saucerest + except AttributeError: + self._saucerest = saucerest.SauceRest( + username=self.sauce_username, + password=self.sauce_apikey + ) + data = dict( name = self._current_test ) diff --git a/test.robot b/test.robot index b1e9740..2262fc5 100644 --- a/test.robot +++ b/test.robot @@ -11,6 +11,7 @@ Test 1 Test 2 Open MyPage + Location Should Be foo [teardown] Close MyPage From 9520a8448e1e22e67fe2dfcb993486940db459e7 Mon Sep 17 00:00:00 2001 From: cohenaa Date: Fri, 20 Mar 2015 15:36:44 -0400 Subject: [PATCH 09/15] Changed var name --- robotpageobjects/base.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/robotpageobjects/base.py b/robotpageobjects/base.py index 733ccdc..94e70c0 100755 --- a/robotpageobjects/base.py +++ b/robotpageobjects/base.py @@ -542,9 +542,8 @@ def _start_test(self, name, attrs): def _end_test(self, name, attrs): self.log("Tag sauce job, %s with %s" %(self.session_id, attrs["status"])) - status = attrs["status"] == "PASS" - self.log(status) - self._saucerest.update_job(self.session_id, dict(passed=status)) + passed = attrs["status"] == "PASS" + self._saucerest.update_job(self.session_id, dict(passed=passed)) def _end_keyword(self, name, attrs): """ Called after every keyword is called in Robot test. From d93f5986aa00d903518b96d033c3a68a25145b7f Mon Sep 17 00:00:00 2001 From: cohenaa Date: Fri, 20 Mar 2015 15:39:25 -0400 Subject: [PATCH 10/15] Formatting --- robotpageobjects/base.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/robotpageobjects/base.py b/robotpageobjects/base.py index 94e70c0..9b27a5f 100755 --- a/robotpageobjects/base.py +++ b/robotpageobjects/base.py @@ -568,10 +568,12 @@ def _end_keyword(self, name, attrs): password=self.sauce_apikey ) - data = dict( - name = self._current_test + self._saucerest.update_job( + session_id, + dict(name=self._current_test) ) - self._saucerest.update_job(session_id, data) + + # Reports whether we've already tagged this job self._register_sauce_job() def __init__(self, *args, **kwargs): From c57916858b94e4d7021341c08c400a53beee2f8f Mon Sep 17 00:00:00 2001 From: cohenaa Date: Fri, 20 Mar 2015 15:44:48 -0400 Subject: [PATCH 11/15] Comments --- robotpageobjects/base.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/robotpageobjects/base.py b/robotpageobjects/base.py index 9b27a5f..a7ef01c 100755 --- a/robotpageobjects/base.py +++ b/robotpageobjects/base.py @@ -547,19 +547,28 @@ def _end_test(self, name, attrs): def _end_keyword(self, name, attrs): """ Called after every keyword is called in Robot test. - We need to get the session ID here, because the browser must - have been open. Also, we only have a session ID when we use - Remote WebDriver (Sauce). + We need to get the session ID here, and initialize sauce tagging + here because the browser must + first be open to get a session ID, and thus be able to tag a + sauce job. We can only be assured of this once we've called + a keyword that's ultimately called SE2Lib's Open Browser + keyword. """ session_id = None + + # Wait until we have a session ID try: session_id = self.session_id except AttributeError: return + # If we haven't tagged this job then, + # initialize a sauce rest object and tag. if not self._sauce_job_registered: self.log("Tag sauce job %s with %s" %(session_id, self._current_test)) + + # Have we initialized a saucerest object already? try: self._saucerest except AttributeError: @@ -568,12 +577,15 @@ def _end_keyword(self, name, attrs): password=self.sauce_apikey ) + # We should have a saucerest object by now + # so go ahead and tag the job with the name of + # the Robot test. self._saucerest.update_job( session_id, dict(name=self._current_test) ) - # Reports whether we've already tagged this job + # Sets the flag whether we've already tagged this job self._register_sauce_job() def __init__(self, *args, **kwargs): From bcb9a17a54858b1620dba36f7f232b99badecb31 Mon Sep 17 00:00:00 2001 From: cohenaa Date: Mon, 23 Mar 2015 15:35:24 -0400 Subject: [PATCH 12/15] Added to README --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index d75308e..bd1250d 100755 --- a/README.md +++ b/README.md @@ -778,6 +778,15 @@ which follows the example of Robot assertions and makes it obvious that the meth Page object assertion methods shouldn't change the state of the page (eg. clicking links, navigating back etc.) and minimal computation, looping etc. State change and computation should be done in page object action/helper methods. In your test, you should get the page to the state where you want it to be using other page object methods, and call the assert method. +## Sauce Labs Cloud Testing Service Integration + +robotframework-pageobjects integrates seamlessly with +[Sauce Labs](http://saucelabs.com/), a cloud service allowing you to run Selenium-based +jobs on a [multitude of browsers and platforms](https://docs.saucelabs.com/reference/platforms-configurator/#/). +Simply set at least the `sauce_apikey`, `sauce_username`, `sauce_platform` and the `browser` +built-in IFT options. See the Built-in options section [above](#built-in-options-for-page) for options +related to running tests in Sauce. + ## Logging Reporting & Debugging ### Robot From d554369914e26850566b1541b8055fb868f13025 Mon Sep 17 00:00:00 2001 From: cohenaa Date: Mon, 23 Mar 2015 15:39:04 -0400 Subject: [PATCH 13/15] README content --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index bd1250d..8eb5331 100755 --- a/README.md +++ b/README.md @@ -787,6 +787,9 @@ Simply set at least the `sauce_apikey`, `sauce_username`, `sauce_platform` and t built-in IFT options. See the Built-in options section [above](#built-in-options-for-page) for options related to running tests in Sauce. +Your page objects will automatically tag your Robot Sauce jobs with the test name and +test status. + ## Logging Reporting & Debugging ### Robot From dbd257ca44f83879faf1240ad119a83122303e8e Mon Sep 17 00:00:00 2001 From: cohenaa Date: Mon, 23 Mar 2015 15:40:33 -0400 Subject: [PATCH 14/15] More corrections --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8eb5331..6768e5c 100755 --- a/README.md +++ b/README.md @@ -787,7 +787,8 @@ Simply set at least the `sauce_apikey`, `sauce_username`, `sauce_platform` and t built-in IFT options. See the Built-in options section [above](#built-in-options-for-page) for options related to running tests in Sauce. -Your page objects will automatically tag your Robot Sauce jobs with the test name and +Your page objects will automatically tag your Robot Sauce jobs with their +associated test names and test status. ## Logging Reporting & Debugging From 0192c11604831c7ebef6ed8c226583c5f0d5fc40 Mon Sep 17 00:00:00 2001 From: cohenaa Date: Mon, 23 Mar 2015 17:11:24 -0400 Subject: [PATCH 15/15] Remoted extranous files --- mypage.py | 5 ----- test.robot | 18 ------------------ 2 files changed, 23 deletions(-) delete mode 100644 mypage.py delete mode 100644 test.robot diff --git a/mypage.py b/mypage.py deleted file mode 100644 index e45eda9..0000000 --- a/mypage.py +++ /dev/null @@ -1,5 +0,0 @@ -from robotpageobjects import Page - - -class MyPage(Page): - uri = "/" diff --git a/test.robot b/test.robot deleted file mode 100644 index 2262fc5..0000000 --- a/test.robot +++ /dev/null @@ -1,18 +0,0 @@ -*** Settings *** -Documentation Some docs - -Library mypage.MyPage - -*** Test Cases *** - -Test 1 - Open MyPage - [teardown] Close MyPage - -Test 2 - Open MyPage - Location Should Be foo - [teardown] Close MyPage - - -