Skip to content

Commit

Permalink
Added a pipeline Python style
Browse files Browse the repository at this point in the history
Added snap-ci with Python style code.

Fixed all bad smells.
  • Loading branch information
ryanfox1985 committed Jan 17, 2017
1 parent 14c0e66 commit e6180b8
Show file tree
Hide file tree
Showing 32 changed files with 42 additions and 135 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
# Testing example

[![Build Status](https://travis-ci.org/tgndevs/testing-example.svg?branch=master)](https://travis-ci.org/tgndevs/testing-example)
[![Style Status](https://app.snap-ci.com/tgndevs/testing-example/branch/master/build_image)](https://app.snap-ci.com/tgndevs/testing-example/branch/master)
[![Coverage Status](https://coveralls.io/repos/github/tgndevs/testing-example/badge.svg?branch=master)](https://coveralls.io/github/tgndevs/testing-example?branch=master)
[![Code Climate](https://codeclimate.com/github/tgndevs/testing-example/badges/gpa.svg)](https://codeclimate.com/github/tgndevs/testing-example)
[![AUR](https://img.shields.io/aur/license/yaourt.svg)]()

This is a proof-of-concept project, to go from a simple project to a tested project with CI using free tools.

The Django application for the demos have been copied from [hjwp/book-example](https://github.com/hjwp/book-example).

In the following section you will see the current status of the project. Step by step, it is going to be updated to include more and more features.

The examples are available at the [Wiki](https://github.com/tgndevs/testing-example/wiki) pages.
Expand Down
2 changes: 1 addition & 1 deletion accounts/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from django.contrib import admin
from django.contrib import admin # noqa: F401

# Register your models here.
3 changes: 1 addition & 2 deletions accounts/authentication.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from accounts.models import User, Token


class PasswordlessAuthenticationBackend(object):

def authenticate(self, uid):
Expand All @@ -11,10 +12,8 @@ def authenticate(self, uid):
except Token.DoesNotExist:
return None


def get_user(self, email):
try:
return User.objects.get(email=email)
except User.DoesNotExist:
return None

2 changes: 0 additions & 2 deletions accounts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ class User(models.Model):
is_authenticated = True



class Token(models.Model):
email = models.EmailField()
uid = models.CharField(default=uuid.uuid4, max_length=40)

6 changes: 1 addition & 5 deletions accounts/tests/test_authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from django.contrib.auth import get_user_model
from accounts.authentication import PasswordlessAuthenticationBackend
from accounts.models import Token

User = get_user_model()


Expand All @@ -13,15 +14,13 @@ def test_returns_None_if_no_such_token(self):
)
self.assertIsNone(result)


def test_returns_new_user_with_correct_email_if_token_exists(self):
email = '[email protected]'
token = Token.objects.create(email=email)
user = PasswordlessAuthenticationBackend().authenticate(token.uid)
new_user = User.objects.get(email=email)
self.assertEqual(user, new_user)


def test_returns_existing_user_with_correct_email_if_token_exists(self):
email = '[email protected]'
existing_user = User.objects.create(email=email)
Expand All @@ -30,7 +29,6 @@ def test_returns_existing_user_with_correct_email_if_token_exists(self):
self.assertEqual(user, existing_user)



class GetUserTest(TestCase):

def test_gets_user_by_email(self):
Expand All @@ -41,9 +39,7 @@ def test_gets_user_by_email(self):
)
self.assertEqual(found_user, desired_user)


def test_returns_None_if_no_user_with_that_email(self):
self.assertIsNone(
PasswordlessAuthenticationBackend().get_user('[email protected]')
)

4 changes: 1 addition & 3 deletions accounts/tests/test_models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.test import TestCase
from django.contrib import auth
from accounts.models import Token

User = auth.get_user_model()


Expand All @@ -10,19 +11,16 @@ def test_user_is_valid_with_email_only(self):
user = User(email='[email protected]')
user.full_clean() # should not raise


def test_no_problem_with_auth_login(self):
user = User.objects.create(email='[email protected]')
user.backend = ''
request = self.client.request().wsgi_request
auth.login(request, user) # should not raise



class TokenModelTest(TestCase):

def test_links_user_with_auto_generated_uid(self):
token1 = Token.objects.create(email='[email protected]')
token2 = Token.objects.create(email='[email protected]')
self.assertNotEqual(token1.uid, token2.uid)

9 changes: 0 additions & 9 deletions accounts/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ def test_redirects_to_home_page(self):
})
self.assertRedirects(response, '/')


def test_adds_success_message(self):
response = self.client.post('/accounts/send_login_email', data={
'email': '[email protected]'
Expand All @@ -24,7 +23,6 @@ def test_adds_success_message(self):
)
self.assertEqual(message.tags, "success")


@patch('accounts.views.send_mail')
def test_sends_mail_to_address_from_post(self, mock_send_mail):
self.client.post('/accounts/send_login_email', data={
Expand All @@ -37,15 +35,13 @@ def test_sends_mail_to_address_from_post(self, mock_send_mail):
self.assertEqual(from_email, 'noreply@superlists')
self.assertEqual(to_list, ['[email protected]'])


def test_creates_token_associated_with_email(self):
self.client.post('/accounts/send_login_email', data={
'email': '[email protected]'
})
token = Token.objects.first()
self.assertEqual(token.email, '[email protected]')


@patch('accounts.views.send_mail')
def test_sends_link_to_login_using_token_uid(self, mock_send_mail):
self.client.post('/accounts/send_login_email', data={
Expand All @@ -60,33 +56,28 @@ def test_sends_link_to_login_using_token_uid(self, mock_send_mail):
self.assertIn(expected_url, body)



@patch('accounts.views.auth')
class LoginViewTest(TestCase):

def test_redirects_to_home_page(self, mock_auth):
response = self.client.get('/accounts/login?token=abcd123')
self.assertRedirects(response, '/')


def test_calls_authenticate_with_uid_from_get_request(self, mock_auth):
self.client.get('/accounts/login?token=abcd123')
self.assertEqual(
mock_auth.authenticate.call_args,
call(uid='abcd123')
)


def test_calls_auth_login_with_user_if_there_is_one(self, mock_auth):
response = self.client.get('/accounts/login?token=abcd123')
self.assertEqual(
mock_auth.login.call_args,
call(response.wsgi_request, mock_auth.authenticate.return_value)
)


def test_does_not_login_if_user_is_not_authenticated(self, mock_auth):
mock_auth.authenticate.return_value = None
self.client.get('/accounts/login?token=abcd123')
self.assertEqual(mock_auth.login.called, False)

1 change: 0 additions & 1 deletion accounts/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,3 @@ def login(request):
if user:
auth.login(request, user)
return redirect('/')

18 changes: 4 additions & 14 deletions functional_tests/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ def setUp(self):
self.browser = webdriver.PhantomJS()
self.browser.implicitly_wait(DEFAULT_WAIT)


def tearDown(self):
if self._test_has_failed():
if not os.path.exists(SCREEN_DUMP_LOCATION):
Expand All @@ -49,29 +48,26 @@ def tearDown(self):
self.browser.switch_to_window(handle)
self.take_screenshot()
self.dump_html()
self.browser.service.process.send_signal(signal.SIGTERM) # kill the specific phantomjs child proc
# kill the specific phantomjs child process
self.browser.service.process.send_signal(signal.SIGTERM)
self.browser.quit()
super().tearDown()


def _test_has_failed(self):
# slightly obscure but couldn't find a better way!
return any(error for (method, error) in self._outcome.errors)


def take_screenshot(self):
filename = self._get_filename() + '.png'
print('screenshotting to', filename)
self.browser.get_screenshot_as_file(filename)


def dump_html(self):
filename = self._get_filename() + '.html'
print('dumping page HTML to', filename)
with open(filename, 'w') as f:
f.write(self.browser.page_source)


def _get_filename(self):
timestamp = datetime.now().isoformat().replace(':', '.')[:19]
return '{folder}/{classname}.{method}-window{windowid}-{timestamp}'.format(
Expand All @@ -82,7 +78,6 @@ def _get_filename(self):
timestamp=timestamp
)


def wait_for(self, function_with_assertion, timeout=DEFAULT_WAIT):
start_time = time.time()
while time.time() - start_time < timeout:
Expand All @@ -93,36 +88,31 @@ def wait_for(self, function_with_assertion, timeout=DEFAULT_WAIT):
# one more try, which will raise any errors if they are outstanding
return function_with_assertion()


def get_item_input_box(self):
return self.browser.find_element_by_id('id_text')


def check_for_row_in_list_table(self, row_text):
table = self.browser.find_element_by_id('id_list_table')
rows = table.find_elements_by_tag_name('tr')
self.assertIn(row_text, [row.text for row in rows])


def assert_logged_in(self, email):
self.browser.find_element_by_link_text('Log out')
navbar = self.browser.find_element_by_css_selector('.navbar')
self.assertIn(email, navbar.text)


def assert_logged_out(self, email):
self.browser.find_element_by_name('email')
navbar = self.browser.find_element_by_css_selector('.navbar')
self.assertNotIn(email, navbar.text)


def create_pre_authenticated_session(self, email):
if self.against_staging:
session_key = create_session_on_server(self.server_host, email)
else:
session_key = create_pre_authenticated_session(email)
## to set a cookie we need to first visit the domain.
## 404 pages load the quickest!
# to set a cookie we need to first visit the domain.
# 404 pages load the quickest!
self.browser.get(self.server_url + "/404_no_such_url/")
cookie_script = "document.cookie = '{name}={value}; path={path}; domain={domain}';\n".format(
name=settings.SESSION_COOKIE_NAME,
Expand Down
2 changes: 1 addition & 1 deletion functional_tests/fabfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
def _get_base_folder(host):
return '~/sites/' + host


def _get_manage_dot_py(host):
return '{path}/virtualenv/bin/python {path}/source/manage.py'.format(
path=_get_base_folder(host)
Expand All @@ -22,4 +23,3 @@ def create_session_on_server(email):
email=email,
))
print(session_key)

13 changes: 1 addition & 12 deletions functional_tests/home_and_list_pages.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
ITEM_INPUT_ID = 'id_text'


class HomePage(object):

def __init__(self, test):
self.test = test


def go_to_home_page(self):
self.test.browser.get(self.test.server_url)
self.test.wait_for(self.get_item_input)
return self


def get_item_input(self):
return self.test.browser.find_element_by_id(ITEM_INPUT_ID)


def start_new_list(self, item_text):
self.go_to_home_page()
inputbox = self.get_item_input()
Expand All @@ -24,7 +22,6 @@ def start_new_list(self, item_text):
list_page.wait_for_new_item_in_list(item_text, 1)
return list_page


def go_to_my_lists_page(self):
self.test.browser.find_element_by_link_text('My lists').click()
self.test.wait_for(lambda: self.test.assertEqual(
Expand All @@ -33,7 +30,6 @@ def go_to_my_lists_page(self):
))



class ListPage(object):

def __init__(self, test):
Expand All @@ -51,37 +47,30 @@ def wait_for_new_item_in_list(self, item_text, position):
[row.text for row in self.get_list_table_rows()]
))


def get_share_box(self):
return self.test.browser.find_element_by_css_selector(
'input[name=email]'
)


def get_shared_with_list(self):
return self.test.browser.find_elements_by_css_selector(
'.list-sharee'
)


def share_list_with(self, email):
self.get_share_box().send_keys(email + '\n')
self.test.wait_for(lambda: self.test.assertIn(
email,
[item.text for item in self.get_shared_with_list()]
))


def get_item_input(self):
return self.test.browser.find_element_by_id(ITEM_INPUT_ID)


def add_new_item(self, item_text):
current_pos = len(self.get_list_table_rows())
self.get_item_input().send_keys(item_text + '\n')
self.wait_for_new_item_in_list(item_text, current_pos + 1)


def get_list_owner(self):
return self.test.browser.find_element_by_id('id_list_owner').text

4 changes: 2 additions & 2 deletions functional_tests/management/commands/create_session.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from django.conf import settings
from django.contrib.auth import BACKEND_SESSION_KEY, SESSION_KEY, get_user_model
User = get_user_model()
from django.contrib.sessions.backends.db import SessionStore
from django.core.management.base import BaseCommand

User = get_user_model()


class Command(BaseCommand):

Expand All @@ -22,4 +23,3 @@ def create_pre_authenticated_session(email):
session[BACKEND_SESSION_KEY] = settings.AUTHENTICATION_BACKENDS[0]
session.save()
return session.session_key

2 changes: 1 addition & 1 deletion functional_tests/server_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import subprocess
THIS_FOLDER = path.dirname(path.abspath(__file__))


def reset_database(host):
subprocess.check_call(
['fab', 'reset_database', '--host=elspeth@{}'.format(host)],
Expand All @@ -19,4 +20,3 @@ def create_session_on_server(host, email):
],
cwd=THIS_FOLDER
).decode().strip()

Loading

0 comments on commit e6180b8

Please sign in to comment.