├── .gitignore ├── LICENSE ├── README.rst ├── manage.py ├── requirements.pip ├── selenium_intro ├── __init__.py ├── selenium_tests │ ├── __init__.py │ ├── models.py │ ├── test.py │ ├── tests │ │ ├── __init__.py │ │ └── auth.py │ └── webdriver.py ├── settings.py ├── templates │ ├── 404.html │ └── 500.html ├── urls.py └── wsgi.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | dev.db 2 | selenium_intro/local_settings.py 3 | *.pyc 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Lincoln Loop, LLC 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. {% comment %} 2 | 3 | ====================== 4 | Django Selenium Intro 5 | ====================== 6 | 7 | ``django-selenium-intro`` is the example project from http://lincolnloop.com/blog/introduction-django-selenium-testing/ 8 | 9 | 10 | .. {% endcomment %} 11 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "selenium_intro.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /requirements.pip: -------------------------------------------------------------------------------- 1 | Django==1.4 2 | selenium 3 | -------------------------------------------------------------------------------- /selenium_intro/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lincolnloop/django-selenium-intro/b316203a2a9114c65274b3d17e5da1e55d56c2a6/selenium_intro/__init__.py -------------------------------------------------------------------------------- /selenium_intro/selenium_tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lincolnloop/django-selenium-intro/b316203a2a9114c65274b3d17e5da1e55d56c2a6/selenium_intro/selenium_tests/__init__.py -------------------------------------------------------------------------------- /selenium_intro/selenium_tests/models.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lincolnloop/django-selenium-intro/b316203a2a9114c65274b3d17e5da1e55d56c2a6/selenium_intro/selenium_tests/models.py -------------------------------------------------------------------------------- /selenium_intro/selenium_tests/test.py: -------------------------------------------------------------------------------- 1 | from django.test import LiveServerTestCase 2 | 3 | class SeleniumTestCase(LiveServerTestCase): 4 | """ 5 | A base test case for selenium, providing hepler methods for generating 6 | clients and logging in profiles. 7 | """ 8 | 9 | def open(self, url): 10 | self.wd.get("%s%s" % (self.live_server_url, url)) -------------------------------------------------------------------------------- /selenium_intro/selenium_tests/tests/__init__.py: -------------------------------------------------------------------------------- 1 | from selenium_intro.selenium_tests.tests.auth import ( 2 | Auth, 3 | ) -------------------------------------------------------------------------------- /selenium_intro/selenium_tests/tests/auth.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | from django.core.urlresolvers import reverse 3 | 4 | from selenium_intro.selenium_tests.test import SeleniumTestCase 5 | from selenium_intro.selenium_tests.webdriver import CustomWebDriver 6 | 7 | # Make sure your class inherits from your base class 8 | class Auth(SeleniumTestCase): 9 | 10 | def setUp(self): 11 | # setUp is where you setup call fixture creation scripts 12 | # and instantiate the WebDriver, which in turns loads up the browser. 13 | User.objects.create_superuser(username='admin', 14 | password='pw', 15 | email='info@lincolnloop.com') 16 | 17 | # Instantiating the WebDriver will load your browser 18 | self.wd = CustomWebDriver() 19 | 20 | def tearDown(self): 21 | # Don't forget to call quit on your webdriver, so that 22 | # the browser is closed after the tests are ran 23 | self.wd.quit() 24 | 25 | # Just like Django tests, any method that is a Selenium test should 26 | # start with the "test_" prefix. 27 | def test_login(self): 28 | """ 29 | Django Admin login test 30 | """ 31 | # Open the admin index page 32 | self.open(reverse('admin:index')) 33 | 34 | # Selenium knows it has to wait for page loads (except for AJAX requests) 35 | # so we don't need to do anything about that, and can just 36 | # call find_css. Since we can chain methods, we can 37 | # call the built-in send_keys method right away to change the 38 | # value of the field 39 | self.wd.find_css('#id_username').send_keys("admin") 40 | # for the password, we can now just call find_css since we know the page 41 | # has been rendered 42 | self.wd.find_css("#id_password").send_keys('pw') 43 | # You're not limited to CSS selectors only, check 44 | # http://seleniumhq.org/docs/03_webdriver.html for 45 | # a more compreehensive documentation. 46 | self.wd.find_element_by_xpath('//input[@value="Log in"]').click() 47 | # Again, after submiting the form, we'll use the find_css helper 48 | # method and pass as a CSS selector, an id that will only exist 49 | # on the index page and not the login page 50 | self.wd.find_css("#content-main") 51 | -------------------------------------------------------------------------------- /selenium_intro/selenium_tests/webdriver.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from selenium.common.exceptions import NoSuchElementException 3 | from selenium.webdriver.support.ui import WebDriverWait 4 | 5 | 6 | #determine the WebDriver module. default to Firefox 7 | try: 8 | web_driver_module = settings.SELENIUM_WEBDRIVER 9 | except AttributeError: 10 | from selenium.webdriver.firefox import webdriver as web_driver_module 11 | 12 | 13 | class CustomWebDriver(web_driver_module.WebDriver): 14 | """Our own WebDriver with some helpers added""" 15 | 16 | def find_css(self, css_selector): 17 | """Shortcut to find elements by CSS. Returns either a list or singleton""" 18 | elems = self.find_elements_by_css_selector(css_selector) 19 | found = len(elems) 20 | if found == 1: 21 | return elems[0] 22 | elif not elems: 23 | raise NoSuchElementException(css_selector) 24 | return elems 25 | 26 | def wait_for_css(self, css_selector, timeout=7): 27 | """ Shortcut for WebDriverWait""" 28 | try: 29 | return WebDriverWait(self, timeout).until(lambda driver : driver.find_css(css_selector)) 30 | except: 31 | self.quit() 32 | -------------------------------------------------------------------------------- /selenium_intro/settings.py: -------------------------------------------------------------------------------- 1 | # Django settings for selenium_intro project. 2 | 3 | import os 4 | import selenium_intro 5 | PROJECT_MODULE_PATH = os.path.dirname(os.path.realpath(selenium_intro.__file__)) 6 | 7 | 8 | DEBUG = True 9 | TEMPLATE_DEBUG = DEBUG 10 | 11 | ADMINS = ( 12 | # ('Your Name', 'your_email@example.com'), 13 | ) 14 | 15 | 16 | MANAGERS = ADMINS 17 | 18 | DATABASES = { 19 | 'default': { 20 | 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. 21 | 'NAME': 'dev.db', # Or path to database file if using sqlite3. 22 | 'USER': '', # Not used with sqlite3. 23 | 'PASSWORD': '', # Not used with sqlite3. 24 | 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. 25 | 'PORT': '', # Set to empty string for default. Not used with sqlite3. 26 | } 27 | } 28 | 29 | # Local time zone for this installation. Choices can be found here: 30 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 31 | # although not all choices may be available on all operating systems. 32 | # In a Windows environment this must be set to your system time zone. 33 | TIME_ZONE = 'America/Chicago' 34 | 35 | # Language code for this installation. All choices can be found here: 36 | # http://www.i18nguy.com/unicode/language-identifiers.html 37 | LANGUAGE_CODE = 'en-us' 38 | 39 | SITE_ID = 1 40 | 41 | # If you set this to False, Django will make some optimizations so as not 42 | # to load the internationalization machinery. 43 | USE_I18N = True 44 | 45 | # If you set this to False, Django will not format dates, numbers and 46 | # calendars according to the current locale. 47 | USE_L10N = True 48 | 49 | # If you set this to False, Django will not use timezone-aware datetimes. 50 | USE_TZ = True 51 | 52 | # Absolute filesystem path to the directory that will hold user-uploaded files. 53 | # Example: "/home/media/media.lawrence.com/media/" 54 | MEDIA_ROOT = '' 55 | 56 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a 57 | # trailing slash. 58 | # Examples: "http://media.lawrence.com/media/", "http://example.com/media/" 59 | MEDIA_URL = '' 60 | 61 | # Absolute path to the directory static files should be collected to. 62 | # Don't put anything in this directory yourself; store your static files 63 | # in apps' "static/" subdirectories and in STATICFILES_DIRS. 64 | # Example: "/home/media/media.lawrence.com/static/" 65 | STATIC_ROOT = '' 66 | 67 | # URL prefix for static files. 68 | # Example: "http://media.lawrence.com/static/" 69 | STATIC_URL = '/static/' 70 | 71 | # Additional locations of static files 72 | STATICFILES_DIRS = ( 73 | # Put strings here, like "/home/html/static" or "C:/www/django/static". 74 | # Always use forward slashes, even on Windows. 75 | # Don't forget to use absolute paths, not relative paths. 76 | ) 77 | 78 | # List of finder classes that know how to find static files in 79 | # various locations. 80 | STATICFILES_FINDERS = ( 81 | 'django.contrib.staticfiles.finders.FileSystemFinder', 82 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 83 | # 'django.contrib.staticfiles.finders.DefaultStorageFinder', 84 | ) 85 | 86 | # Make this unique, and don't share it with anybody. 87 | SECRET_KEY = 'gz)khqe2!x-r#ir36!q)$&o8*klf+zxk7(u(s(-jkv-8g80wv9' 88 | 89 | # List of callables that know how to import templates from various sources. 90 | TEMPLATE_LOADERS = ( 91 | 'django.template.loaders.filesystem.Loader', 92 | 'django.template.loaders.app_directories.Loader', 93 | # 'django.template.loaders.eggs.Loader', 94 | ) 95 | 96 | MIDDLEWARE_CLASSES = ( 97 | 'django.middleware.common.CommonMiddleware', 98 | 'django.contrib.sessions.middleware.SessionMiddleware', 99 | 'django.middleware.csrf.CsrfViewMiddleware', 100 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 101 | 'django.contrib.messages.middleware.MessageMiddleware', 102 | # Uncomment the next line for simple clickjacking protection: 103 | # 'django.middleware.clickjacking.XFrameOptionsMiddleware', 104 | ) 105 | 106 | ROOT_URLCONF = 'selenium_intro.urls' 107 | 108 | # Python dotted path to the WSGI application used by Django's runserver. 109 | WSGI_APPLICATION = 'selenium_intro.wsgi.application' 110 | 111 | TEMPLATE_DIRS = ( 112 | # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". 113 | # Always use forward slashes, even on Windows. 114 | # Don't forget to use absolute paths, not relative paths. 115 | os.path.join(PROJECT_MODULE_PATH, 'templates'), 116 | ) 117 | 118 | INSTALLED_APPS = ( 119 | 'django.contrib.auth', 120 | 'django.contrib.contenttypes', 121 | 'django.contrib.sessions', 122 | 'django.contrib.sites', 123 | 'django.contrib.messages', 124 | 'django.contrib.staticfiles', 125 | 'django.contrib.admin', 126 | 127 | 'selenium_intro.selenium_tests', 128 | ) 129 | 130 | # A sample logging configuration. The only tangible logging 131 | # performed by this configuration is to send an email to 132 | # the site admins on every HTTP 500 error when DEBUG=False. 133 | # See http://docs.djangoproject.com/en/dev/topics/logging for 134 | # more details on how to customize your logging configuration. 135 | LOGGING = { 136 | 'version': 1, 137 | 'disable_existing_loggers': False, 138 | 'filters': { 139 | 'require_debug_false': { 140 | '()': 'django.utils.log.RequireDebugFalse' 141 | } 142 | }, 143 | 'handlers': { 144 | 'mail_admins': { 145 | 'level': 'ERROR', 146 | 'filters': ['require_debug_false'], 147 | 'class': 'django.utils.log.AdminEmailHandler' 148 | } 149 | }, 150 | 'loggers': { 151 | 'django.request': { 152 | 'handlers': ['mail_admins'], 153 | 'level': 'ERROR', 154 | 'propagate': True, 155 | }, 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /selenium_intro/templates/404.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lincolnloop/django-selenium-intro/b316203a2a9114c65274b3d17e5da1e55d56c2a6/selenium_intro/templates/404.html -------------------------------------------------------------------------------- /selenium_intro/templates/500.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lincolnloop/django-selenium-intro/b316203a2a9114c65274b3d17e5da1e55d56c2a6/selenium_intro/templates/500.html -------------------------------------------------------------------------------- /selenium_intro/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, include, url 2 | 3 | from django.contrib import admin 4 | admin.autodiscover() 5 | 6 | urlpatterns = patterns('', 7 | url(r'^admin/', include(admin.site.urls)), 8 | ) 9 | -------------------------------------------------------------------------------- /selenium_intro/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for selenium_intro project. 3 | 4 | This module contains the WSGI application used by Django's development server 5 | and any production WSGI deployments. It should expose a module-level variable 6 | named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover 7 | this application via the ``WSGI_APPLICATION`` setting. 8 | 9 | Usually you will have the standard Django WSGI application here, but it also 10 | might make sense to replace the whole Django WSGI application with a custom one 11 | that later delegates to the Django one. For example, you could introduce WSGI 12 | middleware here, or combine a Django application with an application of another 13 | framework. 14 | 15 | """ 16 | import os 17 | 18 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "selenium_intro.settings") 19 | 20 | # This application object is used by any WSGI server configured to use this 21 | # file. This includes Django's development server, if the WSGI_APPLICATION 22 | # setting points here. 23 | from django.core.wsgi import get_wsgi_application 24 | application = get_wsgi_application() 25 | 26 | # Apply WSGI middleware here. 27 | # from helloworld.wsgi import HelloWorldApplication 28 | # application = HelloWorldApplication(application) 29 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from setuptools import setup, find_packages 3 | 4 | setup( 5 | name="django-selenium-intro", 6 | version='1.0', 7 | description="", 8 | author="Lincoln Loop", 9 | author_email='info@lincolnloop.com', 10 | url='', 11 | packages=find_packages(), 12 | scripts=['manage.py'], 13 | ) 14 | --------------------------------------------------------------------------------