Wrote full test suite for housewars app

This commit is contained in:
Ceferino Patino 2022-09-05 21:01:33 -05:00
commit 58e3b16455
26 changed files with 590 additions and 32 deletions

View file

@ -2,7 +2,7 @@ name: Create release draft
on:
push:
branches: [dev]
branches: [main]
jobs:
draft:

View file

@ -17,10 +17,12 @@ jobs:
env:
DOTENV: '${{ secrets.DOTENV }}'
run: echo "$DOTENV" >> .env
- name: Setup python
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Setup Chrome
uses: nanasess/setup-chromedriver@v1.0.8
- name: Install dependencies
run: python -m pip install -r requirements.txt
- name: Test build

10
.gitignore vendored
View file

@ -260,14 +260,6 @@ cython_debug/
### venv ###
# Virtualenv
# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
[Bb]in
[Ii]nclude
[Ll]ib
[Ll]ib64
[Ll]ocal
[Ss]cripts
pyvenv.cfg
pip-selfcheck.json
venv
# End of https://www.toptal.com/developers/gitignore/api/django,python,pydev,venv

View file

@ -36,7 +36,7 @@ class UserEntryForm(Form):
# Verify that email domain matches
if ('@slps.org' not in email):
raise ValidationError("Please use your school email")
raise ValidationError("Please use your school email.")
# Verify that the email is unique
if UserEntry.objects.filter(email=email).exists():
raise ValidationError(

View file

@ -0,0 +1,23 @@
# Generated by Django 4.0.4 on 2022-09-07 12:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('housewars', '0004_alter_pointsentry_unique_together'),
]
operations = [
migrations.AddField(
model_name='activity',
name='room_number',
field=models.IntegerField(blank=True, default=None, null=True),
),
migrations.AlterField(
model_name='teacher',
name='grade',
field=models.IntegerField(blank=True, choices=[(9, '9th/Freshman'), (10, '10th/Sophomore'), (11, '11th/Junior'), (12, '12th/Senior')], default=None, null=True),
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 4.0.4 on 2022-09-06 13:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('housewars', '0004_alter_pointsentry_unique_together'),
]
operations = [
migrations.AlterField(
model_name='teacher',
name='grade',
field=models.IntegerField(blank=True, choices=[(9, '9th/Freshman'), (10, '10th/Sophomore'), (11, '11th/Junior'), (12, '12th/Senior')], default=None, null=True),
),
]

View file

@ -13,6 +13,7 @@ class Activity(models.Model):
name = models.CharField(max_length=100)
default_quota = models.IntegerField(default=0)
time = models.IntegerField(choices=TimeslotChoices)
room_number = models.IntegerField(blank=True, null=True, default=None)
password = models.CharField(
max_length=100, blank=True, null=True, default=None)

View file

@ -17,7 +17,8 @@ class Teacher(models.Model):
last_name = models.CharField(max_length=100)
house = models.ForeignKey(
House, related_name='teacher', default=None, blank=True, null=True, on_delete=models.SET_NULL)
grade = models.IntegerField(choices=GradeChoices)
grade = models.IntegerField(
choices=GradeChoices, default=None, blank=True, null=True)
activity = models.OneToOneField(
Activity, related_name='teacher', default=None, blank=True, null=True, on_delete=models.SET_NULL)

View file

View file

@ -0,0 +1,33 @@
from django.test import TestCase
from housewars.forms import ActivityForm
from housewars.models import Activity, House
class ActivityFormTest(TestCase):
def setUp(self):
self.activity30 = Activity.objects.create(name='Dodgeball', time=30)
self.activity60 = Activity.objects.create(name='Dodgeball', time=60)
def test_valid_time(self):
form = ActivityForm(data={
'activity1': self.activity30.id,
'activity2': self.activity30.id,
})
self.assertTrue(form.is_valid())
def test_invalid_time(self):
form = ActivityForm(data={
'activity1': self.activity60.id,
'activity2': self.activity30.id,
})
self.assertFalse(form.is_valid())
self.assertEquals(len(form.errors), 1)
def test_no_data(self):
form = ActivityForm(data={})
self.assertFalse(form.is_valid())
self.assertEquals(len(form.errors), 2)

View file

@ -0,0 +1,27 @@
from django.test import TestCase
from housewars.forms import PointsEntryForm
from housewars.models import Activity, House, Award
class ActivityFormTest(TestCase):
def setUp(self):
self.house = House.objects.create(name='Hawk')
self.activity = Activity.objects.create(name='Dodgeball', time=30)
self.activity2 = Activity.objects.create(name='Dodgeball', time=30)
self.award = Award.objects.create(name='1st', activity=self.activity)
def test_valid_time(self):
form = PointsEntryForm(data={
'house': self.house.id,
'activity': self.activity.id,
'award': self.award.id
})
self.assertTrue(form.is_valid())
def test_no_data(self):
form = PointsEntryForm(data={})
self.assertFalse(form.is_valid())
self.assertEquals(len(form.errors), 3)

View file

@ -0,0 +1,38 @@
from django.test import TestCase
from housewars.forms import UserEntryForm
from housewars.models import House
class UserEntryFormTest(TestCase):
def setUp(self):
self.house = House.objects.create(name='Hawk', points=30)
def test_valid(self):
form = UserEntryForm(data={
'first_name': 'Test',
'last_name': 'User',
'email': 'test.user@slps.org',
'grade': 9,
'house': self.house.id
})
self.assertTrue(form.is_valid())
def test_invalid_email(self):
form = UserEntryForm(data={
'first_name': 'Test',
'last_name': 'User',
'email': 'test.user@gmail.com',
'grade': 9,
'house': self.house.id
})
self.assertFalse(form.is_valid())
self.assertEqual(len(form.errors), 1)
def test_no_data(self):
form = UserEntryForm(data={})
self.assertFalse(form.is_valid())
self.assertEquals(len(form.errors), 5)

View file

View file

@ -0,0 +1,23 @@
from django.test import TestCase
from housewars.models import Activity, House, Award, PointsEntry
class HouseModelTest(TestCase):
def setUp(self):
self.house = House.objects.create(name='Hawk', points=30)
self.activity = Activity.objects.create(name='Dodgeball', time=30)
self.award = Award.objects.create(
activity=self.activity, name='1st', points=30)
def test_current_points(self):
PointsEntry.objects.create(
activity=self.activity, house=self.house, award=self.award)
self.assertEquals(self.house.current_points, 30)
def test_total_points(self):
PointsEntry.objects.create(
activity=self.activity, house=self.house, award=self.award)
self.assertEquals(self.house.total_points, 60)

View file

@ -0,0 +1,18 @@
from django.test import TestCase, Client
from django.urls import reverse
from housewars.models import Activity, House, Teacher, UserEntry
class UserEntryTest(TestCase):
def setUp(self):
self.client = Client()
self.house = House.objects.create(name='Hawk', points=30)
self.activity = Activity.objects.create(name='Dodgeball', time=30)
self.entry = UserEntry.objects.create(
first_name='Test', last_name='User', email='test.user@slps.org', grade=9, house=self.house, activity1=self.activity, activity2=self.activity)
self.teacher = Teacher.objects.create(
first_name='Test', last_name='Teacher', grade=9, house=self.house)
def test_mentor(self):
self.assertEquals(self.entry.mentor, self.teacher)

View file

@ -0,0 +1,14 @@
from django.test import TestCase
from django.urls import reverse, resolve
from housewars.views import EntryCreateView, PointsEntryCreateView
class TestUrls(TestCase):
def test_entry_create_is_resolved(self):
url = reverse('housewars:signup')
self.assertEquals(resolve(url).func.view_class, EntryCreateView)
def test_points_entry_create_is_resolved(self):
url = reverse('housewars:add_points')
self.assertEquals(resolve(url).func.view_class, PointsEntryCreateView)

View file

View file

@ -0,0 +1,15 @@
from django.test import TestCase
from housewars.utils import get_random_string
class GetRandomStringTest(TestCase):
def test_string_of_length_8(self):
"""The string generated is of the correct length"""
string = get_random_string(8)
self.assertEqual(len(string), 8)
def test_string_of_length_6(self):
"""The string generated is of the correct length"""
string = get_random_string(6)
self.assertEqual(len(string), 6)

View file

@ -0,0 +1,25 @@
from django.test import TestCase
from housewars.utils import unpack_values
from housewars.models import House
class UnpackValuesTest(TestCase):
def setUp(self):
House.objects.create(name="Hawk")
House.objects.create(name="Snowy")
self.test_queryset = House.objects.all()
def test_valid_row_headers(self):
"""Correctly unpacks the headers into the first index"""
values = unpack_values(self.test_queryset)
self.assertEqual(
values[0], [field.name for field in House._meta.fields])
def test_complete_values(self):
"""Maintains the complete queryset and returns it"""
values = unpack_values(self.test_queryset)
queryset = list(self.test_queryset.values_list())
for row in queryset:
self.assertIn(list(row), values)

View file

@ -1,15 +1,35 @@
from django.test import TestCase
from django.test import TestCase, Client
from django.urls import reverse
from ...models import Activity, House
from housewars.models import Activity, House, Award
class PointsEntryCreateViewTest(TestCase):
def test_view_url_accessible_by_name(self):
response = self.client.get(reverse('housewars:add_points'))
self.assertEqual(response.status_code, 200)
def setUp(self):
self.client = Client()
self.url = reverse('housewars:add_points')
self.house = House.objects.create(name='Hawk')
self.activity = Activity.objects.create(name='Dodgeball', time=30)
self.award = Award.objects.create(activity=self.activity, name='1st')
def test_view_uses_correct_template(self):
response = self.client.get(reverse('housewars:add_points'))
self.assertEqual(response.status_code, 200)
def test_GET(self):
response = self.client.get(self.url)
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, 'housewars/pointsentry_form.html')
def test_POST(self):
response = self.client.post(self.url, {
'house': self.house.id,
'activity': self.activity.id,
'award': self.award.id
})
self.assertEquals(response.status_code, 302)
self.assertEquals(self.house.pointsentry_set.count(), 1)
def test_failed_POST(self):
response = self.client.post(self.url)
self.assertEquals(response.status_code, 200)
self.assertEquals(self.house.pointsentry_set.count(), 0)

View file

@ -1,15 +1,68 @@
from django.test import TestCase
import enum
from django.test import TestCase, Client
from django.urls import reverse
from ...models import Activity, House
from housewars.models import Activity, House
class UserEntryCreateViewTest(TestCase):
def test_view_url_accessible_by_name(self):
response = self.client.get(reverse('housewars:signup'))
self.assertEqual(response.status_code, 200)
def setUp(self):
self.client = Client()
self.url = reverse('housewars:signup')
self.house = House.objects.create(name='Hawk')
self.activity = Activity.objects.create(
name='Dodgeball', time=30, default_quota=5)
self.full_activity = Activity.objects.create(
name='Dodgeball', time=30)
def test_view_uses_correct_template(self):
response = self.client.get(reverse('housewars:signup'))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'housewars/entry_form.html')
def test_POST(self):
user_info_form = {
'user-first_name': 'Test',
'user-last_name': 'User',
'user-email': 'asdfasdf@slps.org',
'user-grade': 9,
'user-house': self.house.id,
'entry_create_view-current_step': 'user'
}
activity_form = {
'activity-activity1': self.activity.id,
'activity-activity2': self.activity.id,
'entry_create_view-current_step': 'activity'
}
steps_data = [user_info_form, activity_form]
for data in steps_data:
self.client.post(self.url, data)
self.assertEquals(self.house.userentry_set.count(), 1)
def test_full_activity_POST(self):
user_info_form = {
'user-first_name': 'Test',
'user-last_name': 'User',
'user-email': 'asdfasdf@slps.org',
'user-grade': 9,
'user-house': self.house.id,
'entry_create_view-current_step': 'user'
}
activity_form = {
'activity-activity1': self.full_activity.id,
'activity-activity2': self.full_activity.id,
'entry_create_view-current_step': 'activity'
}
steps_data = [user_info_form, activity_form]
for data in steps_data:
self.client.post(self.url, data)
self.assertEquals(self.house.userentry_set.count(), 0)
def test_empty_POST(self):
response = self.client.post(self.url)
self.assertEquals(response.status_code, 400)
self.assertEquals(self.house.pointsentry_set.count(), 0)

View file

@ -48,7 +48,7 @@ class EntryCreateView(SessionWizardView):
def done(self, form_list, **kwargs):
UserEntry.objects.create(**self.get_all_cleaned_data())
messages.success(self.request, 'Your entry has been submitted')
messages.success(self.request, 'Your signup has been submitted.')
return redirect('housewars:signup')
def get_success_url(self):

View file

@ -3,6 +3,7 @@ async-generator==1.10
attrs==22.1.0
autopep8==1.6.0
certifi==2022.6.15
charset-normalizer==2.1.1
Django==4.0.4
django-formtools==2.3
django-smart-selects==1.6.0
@ -16,14 +17,17 @@ pycodestyle==2.8.0
PySocks==1.7.1
python-dotenv==0.20.0
reportlab==3.6.11
requests==2.28.1
selenium==4.4.3
sniffio==1.3.0
sortedcontainers==2.4.0
sqlparse==0.4.2
toml==0.10.2
tqdm==4.64.1
trio==0.21.0
trio-websocket==0.9.2
tzdata==2022.1
urllib3==1.26.12
webdriver-manager==3.8.3
whitenoise==6.2.0
wsproto==1.2.0

0
tests/__init__.py Normal file
View file

View file

@ -0,0 +1,62 @@
from django.urls import reverse
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from selenium.webdriver import Chrome
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.expected_conditions import visibility_of
from housewars.models import House, Activity, Award
class UserEntryCreatePageTest(StaticLiveServerTestCase):
def setUp(self):
options = Options()
options.add_argument('--headless')
self.browser = Chrome(
ChromeDriverManager().install(), chrome_options=options)
self.url = self.live_server_url + reverse('housewars:add_points')
self.house = House.objects.create(name='Hawk')
self.activity = Activity.objects.create(name='Dodgeball', time=30)
self.award = Award.objects.create(name='1st', activity=self.activity)
def tearDown(self):
self.browser.close()
def test_user_form_submission(self):
self.browser.get(self.url)
# Wait until browser loads page before continuing
WebDriverWait(self.browser, timeout=5).until(
visibility_of(self.browser.find_element(By.ID, 'header')))
# Fill in form with valid inputs
Select(self.browser.find_element(By.ID, 'id_activity')
).select_by_visible_text('Dodgeball (30)')
Select(self.browser.find_element(By.ID, 'house')
).select_by_visible_text('Hawk')
Select(self.browser.find_element(By.ID, 'id_award')
).select_by_visible_text('1st')
self.browser.find_element(By.ID, 'submit').click()
# Verify that success message is shown
success_text = self.browser.find_element(
By.CSS_SELECTOR, 'div.alert.alert-success').text
self.assertEquals(
success_text, 'Your points entry has been submitted.')
def test_no_awards_on_default(self):
self.browser.get(self.url)
# Wait until browser loads page before continuing
WebDriverWait(self.browser, timeout=5).until(
visibility_of(self.browser.find_element(By.ID, 'header')))
# Verify that only null option is in options
award_select = Select(self.browser.find_element(By.ID, 'id_award'))
self.assertEquals(len(award_select.options), 1)

View file

@ -0,0 +1,189 @@
from django.urls import reverse
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from selenium.webdriver import Chrome
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.expected_conditions import visibility_of
from webdriver_manager.chrome import ChromeDriverManager
from housewars.models import House, Activity, UserEntry
class UserEntryCreatePageTest(StaticLiveServerTestCase):
def setUp(self):
options = Options()
options.add_argument('--headless')
self.browser = Chrome(
ChromeDriverManager().install(), chrome_options=options)
self.url = self.live_server_url + reverse('housewars:signup')
self.house = House.objects.create(name='Hawk')
self.activity30 = Activity.objects.create(
name='Dodgeball', time=30, default_quota=5)
self.activity60 = Activity.objects.create(
name='Volleyball', time=60, default_quota=5)
self.hidden = Activity.objects.create(
name='Hidden', time=30, default_quota=1)
def tearDown(self):
self.browser.close()
def test_user_form_submission(self):
self.browser.get(self.url)
# Wait until browser loads page before continuing
WebDriverWait(self.browser, timeout=5).until(
visibility_of(self.browser.find_element(By.ID, 'header')))
self.assertEqual(self.browser.title, 'House Wars - Signup')
self.browser.find_element(By.ID, 'first-name').send_keys('Test')
self.browser.find_element(By.ID, 'last-name').send_keys('User')
self.browser.find_element(
By.ID, 'email').send_keys('test.user@slps.org')
Select(self.browser.find_element(By.ID, 'grade')
).select_by_visible_text('9th/Freshman')
Select(self.browser.find_element(By.ID, 'house')
).select_by_visible_text('Hawk')
self.browser.find_element(By.ID, 'submit').click()
WebDriverWait(self.browser, timeout=5).until(
visibility_of(self.browser.find_element(By.ID, 'activity1')))
Select(self.browser.find_element(By.ID, 'activity1')
).select_by_visible_text('Dodgeball (30)')
Select(self.browser.find_element(By.ID, 'activity2')
).select_by_visible_text('Dodgeball (30)')
self.browser.find_element(By.ID, 'submit').click()
success_text = self.browser.find_element(
By.CSS_SELECTOR, 'div.alert.alert-success').text
self.assertEquals(success_text, 'Your signup has been submitted.')
def test_user_invalid_email(self):
self.browser.get(self.live_server_url)
# Wait until browser loads page before continuing
WebDriverWait(self.browser, timeout=5).until(
visibility_of(self.browser.find_element(By.ID, 'header')))
# Fill in form with valid inputs except email
self.browser.find_element(By.ID, 'first-name').send_keys('Test')
self.browser.find_element(By.ID, 'last-name').send_keys('User')
self.browser.find_element(
By.ID, 'email').send_keys('test.user@gmail.com')
Select(self.browser.find_element(By.ID, 'grade')
).select_by_visible_text('9th/Freshman')
Select(self.browser.find_element(By.ID, 'house')
).select_by_visible_text('Hawk')
self.browser.find_element(By.ID, 'submit').click()
# Verify that there is a domain error on submit
error_text = self.browser.find_element(
By.CSS_SELECTOR, 'div.alert.alert-danger').text
self.assertEquals(error_text, 'Please use your school email.')
def test_user_used_email(self):
UserEntry.objects.create(first_name='Test', last_name='User', email='test.user@slps.org', grade=9, house=self.house,
activity1=self.activity30, activity2=self.activity30)
self.browser.get(self.live_server_url)
# Wait until browser loads page before continuing
WebDriverWait(self.browser, timeout=5).until(
visibility_of(self.browser.find_element(By.ID, 'header')))
# Fill in form with valid inputs and duplicate email
self.browser.find_element(By.ID, 'first-name').send_keys('Test')
self.browser.find_element(By.ID, 'last-name').send_keys('User')
self.browser.find_element(
By.ID, 'email').send_keys('test.user@slps.org')
Select(self.browser.find_element(By.ID, 'grade')
).select_by_visible_text('9th/Freshman')
Select(self.browser.find_element(By.ID, 'house')
).select_by_visible_text('Hawk')
self.browser.find_element(By.ID, 'submit').click()
# Verify that duplicate error shows up on submit
error_text = self.browser.find_element(
By.CSS_SELECTOR, 'div.alert.alert-danger').text
self.assertEquals(
error_text, 'A signup with this email already exists, if you want to change your signups, please email cpatino8605@slps.org.')
def test_invalid_select_elements_are_not_visible(self):
UserEntry.objects.create(first_name='Test', last_name='User', email='user.exist@slps.org', grade=9, house=self.house,
activity1=self.hidden, activity2=self.activity30)
self.browser.get(self.live_server_url)
# Wait until browser loads page before continuing
WebDriverWait(self.browser, timeout=5).until(
visibility_of(self.browser.find_element(By.ID, 'header')))
# Fill in form with valid inputs
self.browser.find_element(By.ID, 'first-name').send_keys('Test')
self.browser.find_element(By.ID, 'last-name').send_keys('User')
self.browser.find_element(
By.ID, 'email').send_keys('test.user@slps.org')
Select(self.browser.find_element(By.ID, 'grade')
).select_by_visible_text('9th/Freshman')
Select(self.browser.find_element(By.ID, 'house')
).select_by_visible_text('Hawk')
self.browser.find_element(By.ID, 'submit').click()
# Wait until browser loads next step of form before continuing
WebDriverWait(self.browser, timeout=5).until(
visibility_of(self.browser.find_element(By.ID, 'activity1')))
activity1 = Select(self.browser.find_element(By.ID, 'activity1'))
activity2 = Select(self.browser.find_element(By.ID, 'activity2'))
# Assert that full activity is now shown
self.assertRaises(NoSuchElementException,
activity1.select_by_visible_text, 'Hidden (30)')
# Asser that 60 minute activity is not shown in second slot
self.assertRaises(NoSuchElementException,
activity2.select_by_visible_text, 'Volleyball (60)')
# Assert that valid activities are still shown in slot
activity1.select_by_visible_text('Volleyball (60)')
activity2.select_by_visible_text('Hidden (30)')
def test_full_activity_shows_for_other_houses(self):
House.objects.create(name='Snowy')
UserEntry.objects.create(first_name='Test', last_name='User', email='user.exist@slps.org', grade=9, house=self.house,
activity1=self.hidden, activity2=self.hidden)
self.browser.get(self.url)
# Wait until browser loads page before continuing
WebDriverWait(self.browser, timeout=5).until(
visibility_of(self.browser.find_element(By.ID, 'header')))
# Fill in form with valid inputs and separate house
self.browser.find_element(By.ID, 'first-name').send_keys('Test')
self.browser.find_element(By.ID, 'last-name').send_keys('User')
self.browser.find_element(
By.ID, 'email').send_keys('test.user@slps.org')
Select(self.browser.find_element(By.ID, 'grade')
).select_by_visible_text('9th/Freshman')
Select(self.browser.find_element(By.ID, 'house')
).select_by_visible_text('Snowy')
self.browser.find_element(By.ID, 'submit').click()
# Wait until browser loads next form step before continuing
WebDriverWait(self.browser, timeout=5).until(
visibility_of(self.browser.find_element(By.ID, 'activity1')))
# Verify that full activity is still showing for other house
activity1 = Select(self.browser.find_element(By.ID, 'activity1'))
activity2 = Select(self.browser.find_element(By.ID, 'activity2'))
activity1.select_by_visible_text('Hidden (30)')
activity2.select_by_visible_text('Hidden (30)')