From 603e563c1e27b538aa8cc15257578bae0df23250 Mon Sep 17 00:00:00 2001 From: Daron Andrew Edie Date: Mon, 16 Jul 2018 14:22:32 +1000 Subject: [PATCH 1/5] Initial changes to work on python 3 and cleaning up flake8 errors --- PageObjectLibrary/__init__.py | 4 +++- PageObjectLibrary/keywords.py | 9 ++++++--- PageObjectLibrary/locatormap.py | 2 ++ PageObjectLibrary/pageobject.py | 20 ++++++++++-------- setup.py | 36 ++++++++++++++++++--------------- 5 files changed, 43 insertions(+), 28 deletions(-) diff --git a/PageObjectLibrary/__init__.py b/PageObjectLibrary/__init__.py index db5b4ef..748f774 100644 --- a/PageObjectLibrary/__init__.py +++ b/PageObjectLibrary/__init__.py @@ -1,8 +1,10 @@ from __future__ import absolute_import, unicode_literals + from .keywords import PageObjectLibraryKeywords from .pageobject import PageObject from .version import __version__ + class PageObjectLibrary(PageObjectLibraryKeywords): """This project is hosted on github in the repository @@ -142,7 +144,7 @@ class your keywords have access to the following pre-defined | ``*** Test Cases ***`` | Log in to the application | Go to page LoginPage - | Log in as a normal user + | Log in as a normal user | The current page should be DashboardPage """ diff --git a/PageObjectLibrary/keywords.py b/PageObjectLibrary/keywords.py index 13c2374..97e9886 100644 --- a/PageObjectLibrary/keywords.py +++ b/PageObjectLibrary/keywords.py @@ -7,15 +7,19 @@ """ from __future__ import print_function, absolute_import, unicode_literals -import six + import robot.api from robot.libraries.BuiltIn import BuiltIn + +import six + from .pageobject import PageObject try: from urlparse import urlparse except ImportError: from urllib.parse import urlparse + class PageObjectLibraryKeywords(object): ROBOT_LIBRARY_SCOPE = "TEST SUITE" @@ -69,7 +73,7 @@ def the_current_page_should_be(self, page_name): # If we get here, we're not on the page we think we're on raise Exception("Expected page to be %s but it was not" % page_name) - def go_to_page(self, page_name, page_root = None): + def go_to_page(self, page_name, page_root=None): """Go to the url for the given page object. Unless explicitly provided, the URL root will be based on the @@ -125,4 +129,3 @@ def _get_page_object(self, page_name): page = self.builtin.get_library_instance(page_name) return page - diff --git a/PageObjectLibrary/locatormap.py b/PageObjectLibrary/locatormap.py index 36bd7d8..08c48f7 100644 --- a/PageObjectLibrary/locatormap.py +++ b/PageObjectLibrary/locatormap.py @@ -1,6 +1,8 @@ from __future__ import absolute_import, unicode_literals + import six + class LocatorMap(dict): """LocatorMap - a dict-like object that supports dot notation diff --git a/PageObjectLibrary/pageobject.py b/PageObjectLibrary/pageobject.py index a819347..cb35114 100644 --- a/PageObjectLibrary/pageobject.py +++ b/PageObjectLibrary/pageobject.py @@ -1,21 +1,26 @@ from __future__ import absolute_import, unicode_literals + +from abc import ABCMeta +from contextlib import contextmanager + import robot.api from robot.libraries.BuiltIn import BuiltIn -from contextlib import contextmanager -from selenium.webdriver.support.ui import WebDriverWait + from selenium.webdriver.support.expected_conditions import staleness_of -from abc import ABCMeta +from selenium.webdriver.support.ui import WebDriverWait + import six from .locatormap import LocatorMap + class PageObject(six.with_metaclass(ABCMeta, object)): """Base class for page objects Classes that inherit from this class need to define the following class variables: - PAGE_TITLE the title of the page; used by the default + PAGE_TITLE the title of the page; used by the default implementation of _is_current_page PAGE_URL this should be the URL of the page, minus the hostname and port (eg: /loginpage.html) @@ -25,18 +30,18 @@ class PageObject(six.with_metaclass(ABCMeta, object)): provided by this class. It compares the current page title to the class variable PAGE_TITLE. A class can override this method if the page title is not unique or is indeterminate. - + Classes that inherit from this class have access to the following properties: * se2lib a reference to an instance of Selenium2Library * browser a reference to the current webdriver instance * logger a reference to robot.api.logger - * locator a wrapper around the page object's ``_locators`` dictionary + * locator a wrapper around the page object's ``_locators`` dictionary This class implements the following context managers: - * _wait_for_page_refresh + * _wait_for_page_refresh This context manager is designed to be used in page objects when a keyword should wait to return until the html element has been @@ -110,4 +115,3 @@ def _is_current_page(self): self.logger.info(" actual title: '%s'" % actual_title) raise Exception("expected title to be '%s' but it was '%s'" % (expected_title, actual_title)) return False - diff --git a/setup.py b/setup.py index 87e0924..db0b556 100644 --- a/setup.py +++ b/setup.py @@ -1,23 +1,27 @@ # N.B. to push a new version to PyPi, update the version number # in rfhub/version.py and then run 'python setup.py sdist upload' +import sys + from setuptools import setup -execfile('PageObjectLibrary/version.py') +from .version import __version__ + +exec(compile(open('PageObjectLibrary/version.py').read())) setup( - name = 'robotframework-pageobjectlibrary', - version = __version__, - author = 'Bryan Oakley', - author_email = 'bryan.oakley@gmail.com', - url = 'https://github.com/boakley/robotframework-pageobjectlibrary/', - keywords = 'robotframework', - license = 'Apache License 2.0', - description = 'RobotFramework library that implements the Page Object pattern', - long_description = open('README.md').read(), - zip_safe = True, - include_package_data = True, - install_requires = ['robotframework', 'robotframework-selenium2library', 'selenium', 'six'], - classifiers = [ + name='robotframework-pageobjectlibrary', + version=__version__, + author='Bryan Oakley', + author_email='bryan.oakley@gmail.com', + url='https://github.com/boakley/robotframework-pageobjectlibrary/', + keywords='robotframework', + license='Apache License 2.0', + description='RobotFramework library that implements the Page Object pattern', + long_description=open('README.md', encoding='latin-1').read(), + zip_safe=True, + include_package_data=True, + install_requires=['robotframework', 'robotframework-seleniumlibrary', 'selenium', 'six'], + classifiers=[ "Development Status :: 4 - Beta", "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", @@ -27,8 +31,8 @@ "Topic :: Software Development :: Quality Assurance", "Intended Audience :: Developers", ], - packages =[ + packages=[ 'PageObjectLibrary', ], - scripts =[], + scripts=[], ) From a5f9e7aa87244fe3c0c2ba5f9e4e4e1526d3fcc9 Mon Sep 17 00:00:00 2001 From: Daron Andrew Edie Date: Mon, 16 Jul 2018 16:03:58 +1000 Subject: [PATCH 2/5] Wrong usage of exec --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index db0b556..02274bd 100644 --- a/setup.py +++ b/setup.py @@ -4,9 +4,9 @@ from setuptools import setup -from .version import __version__ +# from version import __version__ -exec(compile(open('PageObjectLibrary/version.py').read())) +exec(open('PageObjectLibrary/version.py').read()) setup( name='robotframework-pageobjectlibrary', From 322c37900a17727bc195bdb40dd52c69f45a6499 Mon Sep 17 00:00:00 2001 From: Daron Andrew Edie Date: Tue, 17 Jul 2018 09:27:08 +1000 Subject: [PATCH 3/5] Installs under python 2.6 or greater --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 02274bd..7da23be 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ # N.B. to push a new version to PyPi, update the version number # in rfhub/version.py and then run 'python setup.py sdist upload' -import sys +import io from setuptools import setup @@ -17,7 +17,7 @@ keywords='robotframework', license='Apache License 2.0', description='RobotFramework library that implements the Page Object pattern', - long_description=open('README.md', encoding='latin-1').read(), + long_description=io.open('README.md', encoding='latin-1').read(), zip_safe=True, include_package_data=True, install_requires=['robotframework', 'robotframework-seleniumlibrary', 'selenium', 'six'], From b45c4e903fcaab852ed5b08d64a1b5876785c67d Mon Sep 17 00:00:00 2001 From: Daron Andrew Edie Date: Tue, 17 Jul 2018 11:26:57 +1000 Subject: [PATCH 4/5] Changed demo to use SeleniumLibrary, minor changes to make files flake8 compliant --- PageObjectLibrary/__init__.py | 16 ++++++++-------- PageObjectLibrary/keywords.py | 10 +++++----- PageObjectLibrary/pageobject.py | 4 ++-- demo/resources/HomePage.py | 2 +- demo/resources/LoginPage.py | 4 +++- demo/resources/config.py | 12 +++++++----- demo/tests/demo.robot | 2 +- demo/webapp/demoserver.py | 8 ++++++-- 8 files changed, 33 insertions(+), 25 deletions(-) diff --git a/PageObjectLibrary/__init__.py b/PageObjectLibrary/__init__.py index 748f774..0defb2a 100644 --- a/PageObjectLibrary/__init__.py +++ b/PageObjectLibrary/__init__.py @@ -13,9 +13,9 @@ class PageObjectLibrary(PageObjectLibraryKeywords): *PageObjectLibrary* is a lightweight library which supports using the page object pattern with - [http://robotframework.org/Selenium2Library/doc/Selenium2Library.html|Selenium2Library]. - This library does not replace Selenium2Library; rather, it - provides a framework around which to use Selenium2Library and the + [http://robotframework.org/SeleniumLibrary/doc/SeleniumLibrary.html|SeleniumLibrary]. + This library does not replace SeleniumLibrary; rather, it + provides a framework around which to use SeleniumLibrary and the lower-level [http://selenium-python.readthedocs.org/|Python bindings to Selenium] @@ -32,16 +32,16 @@ class your keywords have access to the following pre-defined attributes and methods: | =Attribute/method= | =Description= | - | ``self.se2lib`` | A reference to the Selenium2Library instance | + | ``self.se2lib`` | A reference to the SeleniumLibrary instance | | ``self.browser`` | A reference to the currently open browser | | ``self.locator`` | A wrapper around the ``_locators`` dictionary | | ``self.logger`` | A reference to the ``robot.api.logger`` instance | | ``self._wait_for_page_refresh()`` | a context manager for doing work that causes a page refresh | - = Using Selenium2Library Keywords = + = Using SeleniumLibrary Keywords = Within your keywords you have access to the full power of - Selenium2Library. You can use ``self.se2lib`` to access the + SeleniumLibrary. You can use ``self.se2lib`` to access the library keywords. The following example shows how to call the ``Capture Page Screenshot`` keyword: @@ -128,7 +128,7 @@ class your keywords have access to the following pre-defined Robot can import it, just like with any other keyword library. When you use the keyword `Go to page`, the keyword will automatically load the keyword library and put it at the front of - the Robot Framework library search order (see + the Robot Framework library search order (see [http://robotframework.org/robotframework/latest/libraries/BuiltIn.html#Set%20Library%20Search%20Order|Set Library Search Order]) In the following example it is assumed there is a second page @@ -137,7 +137,7 @@ class your keywords have access to the following pre-defined | ``*** Settings ***`` | Library PageObjectLibrary - | Library Selenium2Library + | Library SeleniumLibrary | Suite Setup Open browser http://www.example.com | Suite Teardown Close all browsers | diff --git a/PageObjectLibrary/keywords.py b/PageObjectLibrary/keywords.py index 97e9886..dc5733a 100644 --- a/PageObjectLibrary/keywords.py +++ b/PageObjectLibrary/keywords.py @@ -1,7 +1,7 @@ """PageObjectLibrary A library to support the creation of page objects using -selenium and Seleniuim2Library. +selenium and SeleniuimLibrary. """ @@ -34,11 +34,11 @@ def se2lib(self): # can be imported outside the context of a running # suite (ie: by libdoc, robotframework-hub, etc) try: - se2 = self.builtin.get_library_instance("Selenium2Library") + se2 = self.builtin.get_library_instance("SeleniumLibrary") except RuntimeError: - self.builtin.import_library("Selenium2Library") - se2 = self.builtin.get_library_instance("Selenium2Library") + self.builtin.import_library("SeleniumLibrary") + se2 = self.builtin.get_library_instance("SeleniumLibrary") return se2 @@ -94,7 +94,7 @@ def go_to_page(self, page_name, page_root=None): The effect is the same as if you had called the following three keywords: - | Selenium2Library.Go To http://www.example.com/login + | SeleniumLibrary.Go To http://www.example.com/login | Import Library ExampleLoginPage | Set Library Search Order ExampleLoginPage diff --git a/PageObjectLibrary/pageobject.py b/PageObjectLibrary/pageobject.py index cb35114..9e473cd 100644 --- a/PageObjectLibrary/pageobject.py +++ b/PageObjectLibrary/pageobject.py @@ -34,7 +34,7 @@ class variable PAGE_TITLE. A class can override this method if the Classes that inherit from this class have access to the following properties: - * se2lib a reference to an instance of Selenium2Library + * se2lib a reference to an instance of SeleniumLibrary * browser a reference to the current webdriver instance * logger a reference to robot.api.logger * locator a wrapper around the page object's ``_locators`` dictionary @@ -61,7 +61,7 @@ def __init__(self): # test (eg: by libdoc, robotframework-hub, etc) @property def se2lib(self): - return BuiltIn().get_library_instance("Selenium2Library") + return BuiltIn().get_library_instance("SeleniumLibrary") @property def browser(self): diff --git a/demo/resources/HomePage.py b/demo/resources/HomePage.py index c2740aa..7e03739 100644 --- a/demo/resources/HomePage.py +++ b/demo/resources/HomePage.py @@ -1,5 +1,6 @@ from PageObjectLibrary import PageObject + class HomePage(PageObject): """Keywords for the Home page of the demo app @@ -16,4 +17,3 @@ class HomePage(PageObject): # (eg: self.locator.username, etc) _locators = { } - diff --git a/demo/resources/LoginPage.py b/demo/resources/LoginPage.py index d53b57a..5323570 100644 --- a/demo/resources/LoginPage.py +++ b/demo/resources/LoginPage.py @@ -1,6 +1,8 @@ from PageObjectLibrary import PageObject + from robot.libraries.BuiltIn import BuiltIn + class LoginPage(PageObject): PAGE_TITLE = "Login - PageObjectLibrary Demo" PAGE_URL = "/login.html" @@ -24,7 +26,7 @@ def enter_username(self, username): """Enter the given string into the username field""" self.se2lib.input_text(self.locator.username, username) - def enter_password(self,password): + def enter_password(self, password): """Enter the given string into the password field""" self.se2lib.input_text(self.locator.password, password) diff --git a/demo/resources/config.py b/demo/resources/config.py index cd957e3..b4774b1 100644 --- a/demo/resources/config.py +++ b/demo/resources/config.py @@ -1,11 +1,12 @@ import os import sys + class Config(object): - """Configuration variables for this test suite + """Configuration variables for this test suite This creates a variable named CONFIG (${CONFIG} when included - in a test as a variable file. + in a test as a variable file. Example: @@ -28,11 +29,12 @@ def __init__(self): self.demo_root = os.path.abspath(os.path.join(_here, "..")) self.port = 8000 self.root_url = "http://localhost:%s" % self.port - self.username="test user" - self.password="password" + self.username = "test user" + self.password = "password" def __str__(self): return "" % str(self.__dict__) - + + # This creates a variable that robot can see CONFIG = Config() diff --git a/demo/tests/demo.robot b/demo/tests/demo.robot index 087d4de..4b5b8bb 100644 --- a/demo/tests/demo.robot +++ b/demo/tests/demo.robot @@ -5,7 +5,7 @@ | Variables | ../resources/config.py | | Library | PageObjectLibrary -| Library | Selenium2Library +| Library | SeleniumLibrary | Library | Process | | Suite Setup | Start webapp and open browser diff --git a/demo/webapp/demoserver.py b/demo/webapp/demoserver.py index adfc79a..cf471bf 100644 --- a/demo/webapp/demoserver.py +++ b/demo/webapp/demoserver.py @@ -15,6 +15,7 @@ import SocketServer as socketserver from urlparse import urlparse + def main(): parser = argparse.ArgumentParser(description="demo web server") parser.add_argument("-p", "--port", type=int, default=8000, help="port number for the server (default 8000)") @@ -27,11 +28,12 @@ def main(): try: httpd = DemoServer(("", args.port), DemoHandler) print("serving %s on port %s" % (docroot, 8000)) - print ("^C, or visit http://localhost:%s/admin/shutdown to stop" % args.port) + print("^C, or visit http://localhost:%s/admin/shutdown to stop" % args.port) httpd.serve_forever() except KeyboardInterrupt: pass + class DemoHandler(SimpleHTTPRequestHandler): def redirect(self, uri): @@ -50,7 +52,7 @@ def do_POST(self): def do_GET(self): url = urlparse(self.path) print("url: '%s' url.path: '%s'" % (url, url.path)) - if url.path == "" or url.path == "/" : + if url.path == "" or url.path == "/": self.redirect("/login.html") if url.path == "/authenticate": @@ -79,10 +81,12 @@ def get_form_data(self): }) return form + class DemoServer(socketserver.TCPServer): def server_bind(self): self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket.bind(self.server_address) + if __name__ == "__main__": main() From 2bc7e89e7496ce2485becc83b4057ff6bf1f3b88 Mon Sep 17 00:00:00 2001 From: Daron Andrew Edie Date: Tue, 17 Jul 2018 15:30:44 +1000 Subject: [PATCH 5/5] Changed setup to only import open from io module Removed commented out code --- setup.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 7da23be..299197f 100644 --- a/setup.py +++ b/setup.py @@ -1,11 +1,9 @@ # N.B. to push a new version to PyPi, update the version number # in rfhub/version.py and then run 'python setup.py sdist upload' -import io +from io import open from setuptools import setup -# from version import __version__ - exec(open('PageObjectLibrary/version.py').read()) setup( @@ -17,7 +15,7 @@ keywords='robotframework', license='Apache License 2.0', description='RobotFramework library that implements the Page Object pattern', - long_description=io.open('README.md', encoding='latin-1').read(), + long_description=open('README.md', encoding='latin-1').read(), zip_safe=True, include_package_data=True, install_requires=['robotframework', 'robotframework-seleniumlibrary', 'selenium', 'six'],