Git Product home page Git Product logo

holmium.core's Introduction

holmium.core

ci coveralls pypi docs license

Introduction

holmium.core provides utilities for simplifying the creation and maintenance of tests that rely on Selenium.

Nothing beats an example. Conventionally automated tests integrating with python-selenium are written similarly to the following code block (using seleniumhq.org).

import selenium.webdriver
import unittest


class SeleniumHQTest(unittest.TestCase):
    def setUp(self):
        self.driver = selenium.webdriver.Firefox()
        self.url = "http://seleniumhq.org"

    def test_header_links(self):
        self.driver.get(self.url)
        elements = self.driver.find_elements_by_css_selector("div#main_navbar ul li>a")
        self.assertTrue(len(elements) > 0)
        for element in elements:
            self.assertTrue(element.is_displayed())
        expected_link_list = [
          "About",
          "Blog",
          "Documentation",
          "Downloads",
          "English",
          "Projects",
          "Support",
        ]
        actual_link_list = [el.text for el in elements]
        self.assertEqual(sorted(expected_link_list), sorted(actual_link_list))

    def test_projects_selenium_heading(self):
        self.driver.get(self.url)
        projects_link = self.driver.find_element_by_css_selector(
            "nav>a[href='./projects']"
        )
        projects_link.click()
        heading = self.driver.find_element_by_css_selector("p.lead")
        self.assertEqual(heading.text, "Selenium has many projects that combine to form a versatile testing system.")

    def tearDown(self):
        if self.driver:
            self.driver.quit()


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

The above example does what most selenium tests do:

  • initialize a webdriver upon setUp
  • query for one or more web elements using either class name, id, css_selector or xpath
  • assert on the number of occurrences / value of certain elements.
  • tear down the webdriver after each test case

It suffers from the typical web development problem of coupling the test case with the HTML plumbing of the page its testing rather than the functionality its meant to exercise. The concept of PageObjects reduces this coupling and allow for test authors to separate the layout of the page under test and the functional behavior being tested. This separation also results in more maintainable test code (i.e. if an element name changes - all tests don't have to be updated, just the PageObject).

Lets take the above test case for a spin with holmium. Take note of the following:

  • The initialization and reset of the webdriver is delegated to the TestCase base class (alternatively the class could subclass unittest.TestCase and be run with the holmium nose plugin).
  • the page elements are accessed in the test only via Element & ElementMap.
import unittest

from holmium.core import Element, ElementMap, Locators, Page, TestCase


class SeleniumHQPage(Page):
        nav_links = ElementMap(Locators.CSS_SELECTOR, "div#main_navbar ul li>a")
        header_text = Element(Locators.CSS_SELECTOR, "p.lead")


class SeleniumHQTest(TestCase):
        def setUp(self):
                self.page = SeleniumHQPage(self.driver, "http://seleniumhq.org")

        def test_header_links(self):
                self.assertTrue(len(self.page.nav_links) > 0)
                self.assertElementsDisplayed(self.page.nav_links)
                self.assertEqual(
                        sorted(
                                [
                                        "About",
                                        "Blog",
                                        "Documentation",
                                        "Downloads",
                                        "English",
                                        "Projects",
                                        "Support",
                                ]
                        ),
                        sorted(self.page.nav_links.keys()),
                )

        def test_projects_selenium_heading(self):
                self.page.nav_links["Projects"].click()
                self.assertElementTextEqual(
                        self.page.header_text,
                        "Selenium has many projects that combine to form a versatile testing system.",
                )


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

Which can then be executed in a few different ways as shown below.

HO_BROWSER=firefox python test_selenium_hq.py

Feature Summary

  • Automatic provisioning and configuration of webdriver instances based on environment variables
  • Shorthand assertions for web pages (TestCase)
  • Declarative model for defining pages, sections, page elements and element collections (Page Objects)

holmium.core's People

Contributors

alisaifee avatar hyperair avatar kata198 avatar ncjones avatar pehcha avatar pinkie1378 avatar sajnikanth avatar tirkarthi avatar tirsen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

holmium.core's Issues

--holmium-environment requires 'http://' for IP addresses

If I use --holmium-environment=127.0.0.1, it doesn't work. I have to use --holmium-environment=http://127.0.0.1.

My config.py looks like this:

config = {
    "default": {
        "base_url": "{{holmium.environment}}:3000",
        "registration_url": "{{default.base_url}}/users/sign_up",
        "login_url": "{{default.base_url}}/users/sign_in",
        "password": "abc12345",
        "email_domain": "gmail.com"
        }
    }

And I'm trying to run this like -
nosetests tests.py --with-holmium --holmium-browser=firefox --holmium-environment=127.0.0.1 -v.

It is ok when I use -
nosetests tests.py --with-holmium --holmium-browser=firefox --holmium-environment=localhost -v

support for declaring page behaviors

it could be useful to be able to decorate page classes with certain expected behaviors or traits for example:

@redirect(page=LoginPage, action=LoginPage.login...)
@cookie(name="uid", value="yay")
class LoggedInPage(Page)
     .....

Remote driver fails to initialise when used with 'ie', 'safari' as the browser

Hello,

I am trying to run:

nosetests holmium_vistaprint_test.py -v --with-holmium --holmium-browser=ie --holmium-env=default --holmium-remote=http://my_selenium_grid.com:4444/wd/hub
test_login (tests.holmium_test.HolmiumTests) ... SKIP: unable to initialize driver (name: ie) / or (name: safari )


Ran 1 test in 0.004s

OK (SKIP=1)

I can run safari or ie using local browsers, but when trying to run them remotely from our selenium grid there is exact above problem with initialization of the driver.

Unable to install v0.7

When I try to upgrade or explicitly install 0.7, I get this error:

Downloading/unpacking holmium.core from https://pypi.python.org/packages/source/h/holmium.core/holmium.core-0.7.tar.gz#md5=4481b8284957598c4ffb55da4c547cae
  Downloading holmium.core-0.7.tar.gz (305kB): 305kB downloaded
  Running setup.py egg_info for package holmium.core
    Traceback (most recent call last):
      File "<string>", line 16, in <module>
      File "/private/var/folders/mx/t291_0ps2xx7brz47f5lp1p40000gn/T/pip_build_sajnikanth/holmium.core/setup.py", line 16, in <module>
        import versioneer
    ImportError: No module named versioneer
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):

  File "<string>", line 16, in <module>

  File "/private/var/folders/mx/t291_0ps2xx7brz47f5lp1p40000gn/T/pip_build_sajnikanth/holmium.core/setup.py", line 16, in <module>

    import versioneer

ImportError: No module named versioneer

----------------------------------------
Cleaning up...

Results are the same even when I try to do this from holmium.core repo which has versioneer.py

Unable to launch IE

Steps

  • On a windows machine, download IEDriver and add to path
  • Use the sample script here
  • Run using holmium - nosetests test_selenium_hq.py --with-holmium --holmium-browser=ie -v

Actual Results

  • Unable to initialize dirver; refer to screenshot
  • I can launch IE with selenium though; refer to screenshot

Is there any way ho to find element globally under section?

Hey guys,

I think that everybody comes up this kind of situation.
f.e there is a section let it be row in the table and there are some buttons like delete, edit and some dropdown menu with other items. However when you click on this menu element these elements are created not in this row section, but somewhere in the DOM. So Ideally from object prospective I would like to keep those elements in class Row(Sections) however I can't do that because these menu items can not be found under row as they are not there. So my question is it possible to find these menu not under row but globally in the full DOM? I tried out a lot of thing with base_element but is did not work. And actually I have work around for that as create your own looks like Element class which works directly with WebDriver instance, but I'm looking for a more sophisticated way.
here is a piece of code to make my question easier to understand:

<html>
<body>
    <div class"table">
        <div class="row">
            <p> Row 1 </p>
            <button> Delete </button>
            <button> Edit </button>
            <button> Menu... </button>
        </div>
        <div class="row">
            <p> Row 2 </p>
            <button> Delete </button>
            <button> Edit </button>
            <button> Menu... </button>
        </div>
    </div>
    <div class="menu">
        <ul>
            <li> Mark as read </li>
            <li> Spam </li>
        </ul>
    </div
</body>
</html>
class Row(Sections):
    name = Element(Locatots.CLASS, "row")
    buttons = ElementMap(Locatots.TAG_NAME, "button")
    # This will always be an empty dict 
    menu_items = ElementMap(Locators.CSS_SELECTOR, "div.menu li") 

    def mark_as_read(self):
        self.buttons["Menu..."].click()
        # Won't work
        self.menu_items["Mark as read"].click()

Simple problem with HO_BROWSER=ie

When using HO_BROWSER=ie with HO_REMOTE=..., I had problems with our grid. I kept asking them to accept ie as an alias, but it turned out that was not the problem.

Pull request soon.

Mobile testing with Appium

It would be great if I could use holmium.core with Appium to test our mobile app. It would make our tests a lot cleaner and loosely coupled.

page inheritance from a page that defines a facet itself

results in clobbering of the base classe's facet list.

example:

@title(title="test")
class P(Page):
    pass 

@title(title="test two")
class P1(P):
    pass 

@title(title="test three")
class P3(P):
    pass

guess the facets on each page object definition... :S

Four requests to get one section member element

I'm using Section and I find it very useful. But I've noticed that every time when I do:

el = my_page.my_section.my_element

holmium.core does 4 requests to get my_section.root element (when I'd expect 1).

It's easy to reproduce if you'll enable your WebDriver debug/trace logs.

Environment

  • Python 3.6
  • holmium.core==0.8.5
  • selenium==3.6.0
  • Firefox / Geckodriver

Fluent access does not respect false, [], {} return values

I am not a fan of the get_attribute fluent feature. It interferes in weird ways with valid code. Example:
MyPage(Page):
elem = Element(Locators.CSS_SELECTOR, '[name=q]')
def ready(self):
return self.is_displayed()

if ready returns false, then instead of false, self will be returned. There are several reasons why you might want to return false, [], {} or even a valid null, and the get_attrribute takes over. We should fix it at least for false, [], {}. None is a different bag.
Change to:
if not resp == None :
holmium.core.log.debug("method %s returned None, using fluent response" % attr.func_name)
resp = self
return resp

nosetests: error: no such option: --with-holmium on ubuntu 12.04

dmitriy@jenkins:~$ nosetests --with-holmium
/usr/local/lib/python2.7/dist-packages/nose/plugins/manager.py:395: RuntimeWarning: Unable to load plugin holmium = holmium.core:HolmiumNose: six
RuntimeWarning)
Usage: nosetests [options]

nosetests: error: no such option: --with-holmium

Python 2.7.3

Package list:

dmitriy@jenkins:~$ pip freeze Babel==1.3 GnuPGInterface==0.3.2 Jinja2==2.7.3 Markdown==2.3.1 MarkupSafe==0.23 MySQL-python==1.2.5 PAM==0.4.2 PyMySQL==0.6.1 PyYAML==3.10 Twisted-Core==11.1.0 apt-xapian-index==0.44 argh==0.25.0 argparse==1.2.1 chardet==2.0.1 command-not-found==0.2.44 decorator==3.4.0 docutils==0.11 ecdsa==0.11 fake-factory==0.4.0 ghp-import==0.4.1 guess-language==0.2 holmium.core==0.7.6 httplib2==0.7.2 iotop==0.4.4 ipython==2.0.0 keyring==0.9.2 language-selector==0.1 launchpadlib==1.9.12 lazr.restfulclient==0.12.0 lazr.uri==1.0.3 mkdocs==0.9 nose==1.3.4 oauth==1.0.1 ordereddict==1.1 paramiko==1.13.0 pathtools==0.1.2 pyOpenSSL==0.12 pycrypto==2.6.1 pyserial==2.5 python-apt==0.8.3ubuntu7.2 python-debian==0.1.21ubuntu1 pytz==2013.9 requests==2.3.0 robotframework==2.8.5 robotframework-databaselibrary==0.6 robotframework-faker==0.7 robotframework-selenium2library==1.5.0 robotframework-sshlibrary==2.0.2 selenium==2.42.1 simplejson==2.3.2 six==1.7.3 ufw==0.31.1-1 unattended-upgrades==0.1 wadllib==1.3.0 watchdog==0.7.0 wsgiref==0.1.2 zope.interface==3.6.1

Any ideas how to fix it?

SelectEnhancer doesn't work with driver.execute_script('...', select)

When I do:

driver.execute_script('alert(arguments[0].tagName)', page.select_element)

... it raises an exception:

TypeError: <holmium.core.enhancers.SelectEnhancer object at 0x7f7af0ab44e0> is not JSON serializable

Environment

  • Python 3.4
  • holmium.core==0.8.5
  • selenium==3.0.2
  • Firefox / Geckodriver

Problems installing on OS X 10.8 Mountain Lion

I had an issue doing pip install holmium.core for the latest version. I am able to install correctly 0.1.8 version.

Downloading/unpacking holmium.core from https://pypi.python.org/packages/source/h/holmium.core/holmium.core-0.1.8.3.tar.gz#md5=bddb044e188fefa65585a261f37f7277
Downloading holmium.core-0.1.8.3.tar.gz (49kB): 49kB downloaded
Running setup.py egg_info for package holmium.core
Traceback (most recent call last):
File "", line 16, in
File "/private/tmp/pip_build_root/holmium.core/setup.py", line 32, in
long_description=open('README.rst').read() + open('HISTORY.rst').read(),
IOError: [Errno 2] No such file or directory: 'HISTORY.rst'
Complete output from command python setup.py egg_info:
Traceback (most recent call last):

File "", line 16, in

File "/private/tmp/pip_build_root/holmium.core/setup.py", line 32, in

long_description=open('README.rst').read() + open('HISTORY.rst').read(),

IOError: [Errno 2] No such file or directory: 'HISTORY.rst'


Cleaning up...
Command python setup.py egg_info failed with error code 1 in /private/tmp/pip_build_root/holmium.core

FULL LOG:


/usr/local/bin/pip run on Fri Aug 23 11:32:53 2013
Getting page https://pypi.python.org/simple/holmium.core/
URLs to search for versions for holmium.core in /Library/Python/2.7/site-packages:

IOError: [Errno 2] No such file or directory: 'HISTORY.rst'


Cleaning up...

Removing temporary dir /private/tmp/pip_build_root...
Command python setup.py egg_info failed with error code 1 in /private/tmp/pip_build_root/holmium.core

Exception information:
Traceback (most recent call last):
File "/Library/Python/2.7/site-packages/pip-1.4.1-py2.7.egg/pip/basecommand.py", line 134, in main
status = self.run(options, args)
File "/Library/Python/2.7/site-packages/pip-1.4.1-py2.7.egg/pip/commands/install.py", line 236, in run
requirement_set.prepare_files(finder, force_root_egg_info=self.bundle, bundle=self.bundle)
File "/Library/Python/2.7/site-packages/pip-1.4.1-py2.7.egg/pip/req.py", line 1134, in prepare_files
req_to_install.run_egg_info()
File "/Library/Python/2.7/site-packages/pip-1.4.1-py2.7.egg/pip/req.py", line 259, in run_egg_info
command_desc='python setup.py egg_info')
File "/Library/Python/2.7/site-packages/pip-1.4.1-py2.7.egg/pip/util.py", line 670, in call_subprocess
% (command_desc, proc.returncode, cwd))
InstallationError: Command python setup.py egg_info failed with error code 1 in /private/tmp/pip_build_root/holmium.core

Feature Request: Support multiple browsers at the same time by picking the webdriver from the instance on the descriptor call

As it currently implement Holmium will not allow using multiple browsers at the same time. Currently on the latest browser will be used by the one page. Example of code that will not work right now.

from selenium import webdriver

Google(PageObject):
q = PageElement(Locators.CSS_SELECTOR, 'input[name=q]')

firefox = webdriver.Firefox()
firefox_google = Google(firefox, url='http://www.google.com')

chrome = webdriver.Chrome()
chrome_google = Google(chrome, url='http://www.google.com')

firefox_google.q.send_keys('hello world')

expected firefox browser do the search, actual chrome browser does the change

integrate with freshe(r/n)

freshen & fresher integrate rather nicely with nose and aim to ease the job of writing acceptance tests in python. It feels like a nice fit and a good idea to provide some integration.

A sample feature outline

Feature: Google Search
    Scenario: I google for holmium docs and find the rtfd link 
        When I access the page Google at http://www.google.com
        Then I should see the title Google
        And element google_button should be visible 
        When I type holmium python docs in element search_box 
        And press Enter in element search_box
        Then element search_results should be visible 
        And element search_results should have 10 items 
        And the 1st item in search_results should be visible
        And the link of the 1st item in search_results should have text Introduction — holmium 
        When I click the link of the 1st item in search_results 
        Then I should see the title Introduction - holmium 
        When I go back
        And I go forward
        Then I should see the title Introduction - holmium 

which should be executable with

nosetests --with-freshe(r/n) --with-holmium --holmium-browser=firefox my_features

assertConditionWithWait is broken (not hugely useful, misleading documentation)

The docstring for assertConditionWithWait suggests that condition should be an instance of selenium.webdriver.support.expected_conditions, but the WebDriverWait.until call on line 147 evaluates condition(element) only once before execution enters the until() method. This suggests that condition should really be one of the classes in expected_conditions, rather than an instance for the current implementation.

Also, passing in a WebElement in element is not hugely useful -- most of the conditions in expected_conditions expect a locator (By.something, "locator string") rather than an element.

I suggest keeping the documentation for condition as-is (accepting an instance), dropping the element parameter entirely, and just passing condition directly to the WebDriverWait: WebDriverWait(driver, timeout).until(condition). Since WebDriverWait.until simply accepts a callable that takes a single positional parameter (driver), assertConditionWithWait becomes a lot more flexible, even allowing lambdas to be used as a condition.

Feature Request: Extend Replace enhanced function

Consider giving the tester the possibility of replacing or extending the enhanced method. It is nice to have the select autopopulate but sometime you want call some of the web_element methods instead (for example is visible) and the select gets in the way. On the other side of the coin, it might be nice to enhance webelements with our own wrapper.

Multiple browser support

Hey Hey
Holmium is awesome. I think the community would benefit a lot with a built in solution to specify an array of browser configurations per test run.

Currently, --holmium-browser is used, i'm curious is a --holmium-browsers would be possible? Either that, or specifying a list of browser configurations in the desired/holmium capabilities?

Thanks in advance guys!

Sections don't support negative keys

I would like to use negatives keys for sections
like :

last_section = page.my_sections[-1]

but due to this code it is impossible

    def __getitem__(self, idx):

        if idx < 0 or idx >= len(self):
            raise IndexError("Sections index (%d) out of range" % idx)
        for i, _ in enumerate(self):
            if i == idx:
                break
        return self

Feature Request: Extend Replace enhanced function

Consider giving the tester the possibility of replacing or extending the enhanced method. It is nice to have the select autopopulate but sometime you want call some of the web_element methods instead (for example is visible) and the select gets in the way. On the other side of the coin, it might be nice to enhance webelements with our own wrapper.

Avoid deprecation warnings about 'collections.abc' and 'imp' imports

During tests execution in virtualenv created with python 3.9 following warnings are printed out:

../..../lib/python3.9/site-packages/holmium/core/testcase.py:9
  /.../lib/python3.9/site-packages/holmium/core/testcase.py:9: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
    import imp

../.../lib/python3.9/site-packages/holmium/core/pageobject.py:593
  /.../lib/python3.9/site-packages/holmium/core/pageobject.py:593: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3, and in 3.10 it will stop working
    class Sections(Section, collections.Sequence):

Would be nice to adjust code to avoid these warnings

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.