开发者

Testing authentication UI in selenium

I am using selenium with pylons to test user interactions. Everything was fine until I added testing for an authentication UI -- a login screen with user name and password. It appears that Chrome (the browser I am using under selenium) is popping up a dialog to request whether to store the credentials. All the tests fail after this point.

Does anyone have any pointers on using selenium with authentication/UIs that may pop up dialog boxes? I am aware of this selenium warning. "Don't do this. Do this other thing instead" would be an acceptable answer.


project/tests/lib/selenium/login_page.py

"""
LoginPage represents the page where users are authenticated for the application
"""


class LoginPage(object):
    """This is the LoginPage class."""

    login = '/authentication/login'
    logout = '/authentication/logout'
    home = '/home'

    def __init__(self, browser):
        self._browser = browser

    def goto_login(self, baseurl):
        return self._browser.go_to(baseurl + self.login)

    def goto_logout(self, baseurl):
        return self._browser.go_to(baseurl + self.logout)

    def goto_home(self, baseurl):
        return self._browser.go_to(baseurl + self.home)

    def enter_credentials(self, username, password):
        element_by_id = self._browser.get_element_by_id
        element_by_id("login").value = username
        element_by_id("password").value = password

    def submit_form(self):
        element = self._browser.get_element_by_id('submit')
        return self._browser.click(element)

project/tests/selenium/test_login_page.py

"""Tests the login page."""

import nose.tools as nt
from nose.plugins.skip import SkipTest

from assess.tests.selenium import SeleniumTestBase
from assess.tests.lib.selenium.login_page import LoginPage


class TestLoginPage(SeleniumTestBase):
    """Tests the login page."""

    def _login_page(self):
        return self.baseurl + '/authentication/login'

    def setUp(self):
        nt.set_trace()
        super(TestLoginPage, self).setUp()
        self.page = LoginPage(self.browser)

    def tearDown(self):
        super(TestLoginPage, self).tearDown()
        self.page = None

    def test_login_page_fail(self):
        # Logout before beginning test
        self.page.goto_logout(self.baseurl)

        self.page.goto_login(self.baseurl)
        nt.assert_true(self.browser.get_url().startswith(self._login_page()))
        self.page.enter_credentials('foo', 'random')
        self.page.submit_form()

    def test_login_page_success(self):
        # Logout before beginning test
        self.page.goto_logout(self.baseurl)

        self.page.goto_login(self.baseurl)
        nt.assert_true(self.browser.get_url().startswith(self._login_page()))
        self.page.enter_credentials('user', 'good-password')
        self.page.submit_开发者_JS百科form()

project/templates/login.html.mako

<%inherit file="/layout.html.mako" />

${h.stylesheet_link('login.css')}

<form action="/__do_login" method="POST">
    <fieldset>
        <p>
        <label for="login">User name</label><br />
        <input id="login" name="login" type="text" maxlength="40" value="" />
        </p>
        <p>
        <label for="password">Password</label><br />
        <input id="password" name="password" type="password" maxlength="40" value="" />
        </p>
        <p class="submit">
        <input id="submit" type="submit" value="Submit" />
        </p>
    </fieldset>
</form>

etc/who.ini

[general]
request_classifier = repoze.who.classifiers:default_request_classifier
challenge_decider = repoze.who.classifiers:default_challenge_decider

[identifiers]
plugins = foo
          friendly_form;browser

[authenticators]
plugins = foo 

[challengers]
plugins = friendly_form;browser
          foo

[plugin:foo]
use = ...
host = ...
logout_path_regex = /authentication/logout
httponly = True

[plugin:friendly_form]
use = repoze.who.plugins.friendlyform:FriendlyFormPlugin
login_form_url = /authentication/login
login_handler_path = /__do_login
post_login_url = /home
logout_handler_path = /authentication/logout
post_logout_url = /authentication/login

project/config/routing.py

def make_map(config):
    """Create, configure and return the routes Mapper"""
    mapper = Mapper(directory=config['pylons.paths']['controllers'],
                 always_scan=config['debug'])
    mapper.minimization = False
    mapper.explicit = False

    # The ErrorController route (handles 404/500 error pages); it should
    # likely stay at the top, ensuring it can always be resolved
    mapper.connect('/error/{action}', controller='error')
    mapper.connect('/error/{action}/{sid}', controller='error')

    # CUSTOM ROUTES HERE

...
    mapper.connect('/authentication/login',
                   controller='authentication',
                   action='index')
    mapper.connect('/authentication/logout',
                   controller='authentication',
                   action='logout')

project/controllers/authentication.py

"""
This module contains the login controller.
"""

import logging

from pylons.controllers.util import redirect
from pylons import url, tmpl_context as c, request

from project.lib.base import BaseController
from project.lib.authorize import user_is_authenticated

logger = logging.getLogger(__name__)


class AuthenticationController(BaseController):
    """ This controller serves the login page."""
    template = '/login.html.mako'

    def index(self):
        return self.render(self.template)

    def validate(self):
        """ render a login page if we're not logged in """
        c.came_from = request.params.get("came_from", url("home"))

        # If we're already authenticated, redirect us to where we started.
        if user_is_authenticated():
            msg = "User is authenticated: redirecting to %s" % c.came_from
            logger.info(msg)
            redirect(c.came_from)

        msg = "User is not authenticated: rendering %s" % self.template
        logger.info(msg)
        return self.render(self.template)

project/lib/authorize.py

''' Helper functions for the authorization and authentication mechanisms. '''
from pylons import request
from decorator import decorator
from pylons.controllers.util import abort


def user_is_authenticated():
    """ Returns True if is authenticated, else returns False.
    """
    identity = request.environ.get('repoze.who.identity')
    return identity and 'xxx' in identity


@decorator
def authenticated(func, *args, **kwargs):
    """ Check if is authenticated.  If not authenticated, abort with
        401 status.
    """
    if not user_is_authenticated():
        abort(401, 'You are not authenticated')
    return func(*args, **kwargs)


Ideally you should use a profile for your tests and in this profile you should specify to never ask for saving the credentials. Unfortunately it is impossible to specify a customized Chrome profile with Selenium 1, but it is possible to do for Firefox in Selenium 1 or move to Selenium 2 (WebDriver) - see this thread for more information

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜