Roman Prokofyev

New-Based Trading at LGT Capital Partners. Tech Advisor at FAIRTIQ.

Django-selenium jquery-like element selector

23 Feb 2013 » django-selenium, django, selenium, en

You might know about django-selenium, the tool we developed for django to provide the ability to write selenium tests as easy as normal django tests. Alongside we’ve also added numerous helper methods that incorporated common use cases of our (and hopefully other people’s) selenium test writing experience (full list of methods is available at: https://django-selenium.readthedocs.org/en/latest/#mydriver-class)

Until recently, there still remained an annoying problem: django-selenium’s find method could only operate on single (first) element of the css selector. That means when you need other element than the first one, you had to switch to native selenium’s find_elements_by_css_selector method. Luckily, this problem is solved beautifully in jquery, where by default you can operate on whole list of elements returned by selector, but at the same time single-element operations are applied to the first element of the list.

So, I decided to implement the same behaviour for django-selenium. In python it can be easily achieved using the special getattribute method, which allows to intercept every attribute access. Here is the first version of code for SeleniumElement that serves as a proxy to real selenium elements in django-selenium 0.9.5:

class SeleniumElement(object):
    def __init__(self, elements, selector):
        self.elements = elements
        self.selector = selector
    def __getattribute__(self, name):
        """
        Pass ``__getattribute__`` directly to the first array element.
        """
        attr = object.__getattribute__(self, 'elements')[0].__getattribute__(name)
        return attr
    def __getitem__(self, key):
        """
        Return item from the internal sequence, bypassing ``__getattribute__``
        """
        return object.__getattribute__(self, 'elements')[key]

Shorty, __getattribute_ method overwrite will proxy every function call/attribute access to the underlying first element of the element list returned by selector, and __getitem__ method overwrite will be called each time the element is accessed by index, thus returning the underlying element from the elements list.

Example from the tests:

def test_multiple_elements(self):
    test_list = ['one string', 'another string']
    se = SeleniumElement(test_list, 'selector')
    self.assertEquals(se.replace('one', 'two'), 'two string')
    for i, elem in enumerate(se):
        self.assertEquals(elem, test_list[i])