more tests

This commit is contained in:
Tom Irgang 2023-04-28 11:54:13 +02:00
parent beb795dfb1
commit 4dbcee4417
19 changed files with 343 additions and 47 deletions

View file

@ -17,7 +17,8 @@
"KeycloakOIDCAB",
"Keycloak",
"tabindex",
"onetimelogin"
"onetimelogin",
"Ammeldelink"
],
"ignoreWords": [],
"import": []

View file

@ -88,7 +88,6 @@ TEMPLATES = [
WSGI_APPLICATION = "rc_hip.wsgi.application"
# Database
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases

View file

@ -7,9 +7,9 @@ from hashlib import sha256
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from django.contrib.auth import get_user_model
from selenium.webdriver.common.by import By
# from selenium.webdriver.chrome.webdriver import WebDriver
from selenium.webdriver.firefox.webdriver import WebDriver
from .models import CustomUser, Guest, OneTimeLogin
from selenium.webdriver.chrome.webdriver import WebDriver
from selenium.webdriver import ActionChains, Keys
from .models import CustomUser, OneTimeLogin
class WorkflowTests(StaticLiveServerTestCase):
@ -22,7 +22,8 @@ class WorkflowTests(StaticLiveServerTestCase):
def setUpClass(cls):
super().setUpClass()
cls.selenium = WebDriver()
cls.selenium.implicitly_wait(10)
cls.selenium.implicitly_wait(50)
cls.selenium.maximize_window()
@classmethod
def tearDownClass(cls):
@ -32,6 +33,8 @@ class WorkflowTests(StaticLiveServerTestCase):
def setUp(self):
super().setUp()
self.action_chains = ActionChains(self.selenium)
self.password = "ATestPassword"
user = CustomUser.objects.get(pk=3)
user.set_password(self.password)
@ -62,7 +65,7 @@ class WorkflowTests(StaticLiveServerTestCase):
self.selenium.get(f"{self.live_server_url}/")
# Click on a register link
self.selenium.find_element(By.CLASS_NAME, "register_link").click()
time.sleep(1)
time.sleep(2)
# Enter new device
# Enter guest mail address
mail = self.selenium.find_element(By.NAME, "mail")
@ -76,6 +79,10 @@ class WorkflowTests(StaticLiveServerTestCase):
# Enter a error description
error = self.selenium.find_element(By.NAME, "error")
error.send_keys("A description of the issue\nof the device.")
# Scroll to the bottom of the page
self.selenium.find_element(
By.TAG_NAME, "body").send_keys(Keys.CONTROL, Keys.END)
time.sleep(1)
# Skip device picture and device type plate
# No follow up repair
# Accept repair conditions
@ -121,6 +128,10 @@ class WorkflowTests(StaticLiveServerTestCase):
# Enter a error description
error = self.selenium.find_element(By.NAME, "error")
error.send_keys("Another issue description.")
# Scroll to the bottom of the page
self.selenium.find_element(
By.TAG_NAME, "body").send_keys(Keys.CONTROL, Keys.END)
time.sleep(1)
# Add device picture
test_script_folder = Path(__file__).resolve().parent
fixtures_folder = os.path.join(test_script_folder, "fixtures")

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,6 @@
{% load static %}
<!DOCTYPE html>
<html lang="de">
{% load static %}
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="description"
@ -10,18 +10,65 @@
<meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<link rel="stylesheet" href="{% static 'repapp/css/bootstrap.min.css' %}" />
<script src="{% static 'repapp/css/bootstrap.min.js' %}"></script>
<title>RepApp
<title>RepApp -
{% block title %}
{% endblock title %}
</title>
<link rel="stylesheet" href="{% static 'repapp/css/bootstrap.min.css' %}" />
{% block head %}
{% endblock head %}
</head>
<body>
<div class="container">
<header class="d-flex flex-wrap justify-content-center py-3 mb-4 border-bottom">
<a href="{% url 'index' %}"
class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-dark text-decoration-none">
<img src="{% static 'repapp/images/logo.jpg' %}"
height="50px"
width="168px"
alt="Repair-Café Logo">
</a>
<ul class="nav nav-pills">
<li class="nav-item">
<a href="https://www.repaircafe-hilpoltstein.de/" class="nav-link">Repair-Café</a>
</li>
<li class="nav-item">
<a href="{% url 'index' %}" class="nav-link">Anmeldung</a>
</li>
{% if user.is_authenticated %}
<li class="nav-item">
<a href="{% url 'logout' %}" class="nav-link">Abmelden</a>
</li>
{% else %}
<li class="nav-item">
<a href="{% url 'login' %}" class="nav-link">Gast Anmeldung</a>
</li>
<li class="nav-item">
<a href="{% url 'member_login' %}" class="nav-link">Mitglieder Anmeldung</a>
</li>
{% endif %}
</ul>
</header>
</div>
<div class="position-relative overflow-hidden p-3 p-md-5 m-md-3 text-center"
style="background-image: url({% static 'repapp/images/header_background.jpg' %});
background-repeat: no-repeat;
background-size: auto 100%;
background-position: center">
<div class="col-md-5 p-lg-5 mx-auto my-5">
<h1 class="display-4 font-weight-normal"
style="font-size: 60px;
font-weight: 700;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 1);
color: white">
{% block page_title %}
{% endblock page_title %}
</h1>
</div>
</div>
<div class="container">
<div class="row justify-content-center">
<div class="col-8">
<div class="col-12">
{% if messages %}
{% for message in messages %}<div class="alert alert-primary" role="alert">{{ message }}</div>{% endfor %}
{% endif %}
@ -30,14 +77,18 @@
</div>
</div>
</div>
<footer>
{% if user.is_authenticated %}
<a href="{% url 'logout' %}">Gast Logout</a>
<a href="{% url 'member_login' %}">Mitarbeiter Logout</a>
{% else %}
<a href="{% url 'login' %}">Gast Login</a>
<a href="{% url 'member_login' %}">Mitarbeiter Login</a>
{% endif %}
</footer>
<div class="container">
<footer class="d-flex flex-wrap justify-content-between align-items-center py-3 my-4 border-top">
<p class="col-md-4 mb-0 text-muted">© 2023 Repair-Café Hilpoltstein</p>
<ul class="nav col-md-4 justify-content-end">
<li class="nav-item">
<a href="https://www.repaircafe-hilpoltstein.de/impressum"
class="nav-link px-2 text-muted">Impressum & Datenschutz</a>
</li>
</ul>
</footer>
</div>
<script src="{% static 'repapp/js/jquery-3.6.4.min.js' %}"></script>
<script src="{% static 'repapp/js/bootstrap.min.js' %}"></script>
</body>
</html>

View file

@ -0,0 +1,81 @@
<!DOCTYPE html>
{% load static %}
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="description"
content="{% block description %} {% endblock description %}" />
<meta name="keywords"
content="{% block keywords %} {% endblock keywords %}" />
<meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<link rel="stylesheet" href="{% static 'repapp/css/bootstrap.min.css' %}" />
<title>RepApp -
{% block title %}
Bootstrap
{% endblock title %}
</title>
<link rel="stylesheet" href="{% static 'repapp/css/bootstrap.min.css' %}" />
{% block head %}
{% endblock head %}
</head>
<body>
<div class="container">
<header class="d-flex flex-wrap justify-content-center py-3 mb-4 border-bottom">
<a href="{% url 'index' %}"
class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-dark text-decoration-none">
<img src="{% static 'repapp/images/logo.jpg' %}"
height="50px"
width="168px"
alt="Repair-Café Logo">
</a>
<ul class="nav nav-pills">
<li class="nav-item">
<a href="https://www.repaircafe-hilpoltstein.de/" class="nav-link">Repair-Café</a>
</li>
<li class="nav-item">
<a href="{% url 'index' %}" class="nav-link">Anmeldung</a>
</li>
{% if user.is_authenticated %}
<li class="nav-item">
<a href="{% url 'logout' %}" class="nav-link">Abmelden</a>
</li>
{% else %}
<li class="nav-item">
<a href="{% url 'login' %}" class="nav-link">Gast Anmeldung</a>
</li>
<li class="nav-item">
<a href="{% url 'member_login' %}" class="nav-link">Mitglieder Anmeldung</a>
</li>
{% endif %}
</ul>
</header>
</div>
<div class="position-relative overflow-hidden p-3 p-md-5 m-md-3 text-center"
style="background-image: url({% static 'repapp/images/header_background.jpg' %});
background-repeat: no-repeat;
background-size: auto 100%;
background-position: center">
<div class="col-md-5 p-lg-5 mx-auto my-5">
<h1 class="display-4 font-weight-normal"
style="font-size: 60px;
font-weight: 700;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 1);
color: white">Anmeldung</h1>
</div>
</div>
<div class="container">
<div class="row justify-content-center">
<div class="col-8">
{% if messages %}
{% for message in messages %}<div class="alert alert-primary" role="alert">{{ message }}</div>{% endfor %}
{% endif %}
{% block content %}
{% endblock content %}
</div>
</div>
</div>
<script src="{% static 'repapp/js/jquery-3.6.4.min.js' %}"></script>
<script src="{% static 'repapp/js/bootstrap.min.js' %}"></script>
</body>
</html>

View file

@ -2,9 +2,10 @@
{% block title %}
- {{ device.device }}
{% endblock title %}
{% block page_title %}
Gerät: {{ device.device }}
{% endblock page_title %}
{% block content %}
<h1 class="mt-2">Gerät: {{ device.device }}</h1>
<hr class="mt-0 mb-4">
<table class="table">
<tbody>
<tr>

View file

@ -1,10 +1,11 @@
{% extends "repapp/base.html" %}
{% block title %}
- Gast Daten
- Ihre Daten
{% endblock title %}
{% block page_title %}
Ihre Daten
{% endblock page_title %}
{% block content %}
<h1 class="mt-2">Ihre Daten</h1>
<hr class="mt-0 mb-4">
<table class="table">
<tbody>
<tr>

View file

@ -282,8 +282,11 @@
}
</style>
{% endblock head %}
{% block page_title %}
Anmeldung
{% endblock page_title %}
{% block content %}
<h1 class="mt-2">Repair-Cafés</h1>
<h1 class="mt-2">Repair-Café auswählen</h1>
<hr class="mt-0 mb-4">
<ul class="list-group">
{% for cafe in object_list %}

View file

@ -1,17 +1,16 @@
{% extends "repapp/base.html" %}
{% block title %}
- Mitarbeiter Login
Mitarbeiter Anmeldung
{% endblock title %}
{% block page_title %}
Mitarbeiter
{% if user.is_authenticated %}
Anmelden
{% else %}
Abmelden
{% endif %}
{% endblock page_title %}
{% block content %}
<h1 class="mt-2">
Mitarbeiter
{% if user.is_authenticated %}
Logout
{% else %}
Login
{% endif %}
</h1>
<hr class="mt-0 mb-4">
<div>
{% if user.is_authenticated %}
<p>Angemeldeter Benutzer: {{ user.email }}</p>

View file

@ -3,9 +3,10 @@
{% block title %}
- Gerät anmelden
{% endblock title %}
{% block page_title %}
Gerät anmelden
{% endblock page_title %}
{% block content %}
<h1 class="mt-2">Gerät anmelden</h1>
<hr class="mt-0 mb-4">
<form action="{% url 'register_device' cafe.id %}"
method="post"
enctype="multipart/form-data">

View file

@ -1,9 +1,10 @@
{% extends "repapp/base.html" %}
{% block title %}
{% endblock title %}
{% block page_title %}
Anmeldung erfolgreich!
{% endblock page_title %}
{% block content %}
<h1 class="mt-2">Anmeldung erfolgreich!</h1>
<hr class="mt-0 mb-4">
<p>
Vielen Dank für die Anmeldung des Geräts {{ device.device }} - {{ device.manufacturer }} zur Reparatur im Repair-Café am {{ cafe.event_date|date }}.
</p>

View file

@ -3,9 +3,10 @@
{% block title %}
- Gast anmelden
{% endblock title %}
{% block page_title %}
Ihre Daten
{% endblock page_title %}
{% block content %}
<h1 class="mt-2">Ihre Daten</h1>
<hr class="mt-0 mb-4">
<form action="{% url 'register_guest' cafe.id device.identifier mail %}"
method="post">
{% csrf_token %}

View file

@ -1,17 +1,25 @@
"""
Tests for RepApp.
"""
import time
from hashlib import sha256
from datetime import datetime, timedelta
import django.utils.timezone
from django.test import TestCase
from django.test import TestCase, RequestFactory
from django.test import Client
from django.urls import reverse
from django.core import mail
from django.contrib.messages.storage.fallback import FallbackStorage
from .backends import OneTimeLoginBackend, EmailBackend, create_repapp_user
from .models import (OneTimeLogin, CustomUser, Cafe, Organisator, Reparateur)
from .models import (OneTimeLogin, CustomUser, Cafe,
Organisator, Reparateur, Device, Guest)
from .views import (send_one_time_login_mail,
send_confirmation_mails,
send_guest_account_mail,
is_member,
create_one_time_login)
class OneTimeLoginTest(TestCase):
@ -192,6 +200,8 @@ class ViewsTest(TestCase):
fixtures = ["cafe-data.json"]
def setUp(self):
self.factory = RequestFactory()
cafe = Cafe(location="neuer Ort", address="neue Adresse",
event_date=django.utils.timezone.now())
cafe.save()
@ -281,3 +291,114 @@ class ViewsTest(TestCase):
self.assertContains(response, "Abmelden")
# ensure user is displayed
self.assertContains(response, "testuser@example.com")
def test_send_one_time_login_mail(self):
"""
Test that a mail is sent with the right link included.
"""
secret = "A_SECRET"
mail_address = "guest@example.com"
request = self.factory.get('/')
setattr(request, 'session', 'session')
setattr(request, '_messages', FallbackStorage(request))
send_one_time_login_mail(secret, mail_address, request)
self.assertEqual(len(mail.outbox), 1)
self.assertTrue("Einmal-Ammeldelink" in mail.outbox[0].subject)
self.assertEqual(mail.outbox[0].to, [mail_address])
self.assertTrue(f'/onetimelogin/{secret}/' in mail.outbox[0].body)
def test_send_confirmation_mails(self):
"""
Test that the confirmation mails are sent.
"""
device = Device.objects.get(pk=2)
guest = Guest.objects.get(pk=4)
cafe = Cafe.objects.get(pk=1)
organisator = Organisator(
name="Orga",
mail="orga@example.com"
)
organisator.save()
request = self.factory.get('/')
setattr(request, 'session', 'session')
setattr(request, '_messages', FallbackStorage(request))
send_confirmation_mails(device, guest, cafe, request)
self.assertEqual(len(mail.outbox), 2)
self.assertEqual(mail.outbox[0].subject,
f"Neues Gerät { device.device }")
self.assertEqual(mail.outbox[0].to, [organisator.mail])
self.assertTrue("Geräteanmeldung" in mail.outbox[1].subject)
self.assertEqual(mail.outbox[1].to, [guest.mail])
self.assertTrue("Geräteanmeldung" in mail.outbox[1].body)
def test_send_guest_account_mail(self):
"""
Test that an account creation mail was sent.
"""
password = "A_SECRET"
guest = Guest.objects.get(pk=4)
request = self.factory.get('/')
setattr(request, 'session', 'session')
setattr(request, '_messages', FallbackStorage(request))
send_guest_account_mail(guest, password, request)
self.assertEqual(len(mail.outbox), 1)
self.assertTrue("Benutzerkonto" in mail.outbox[0].subject)
self.assertEqual(mail.outbox[0].to, [guest.mail])
self.assertTrue(f'{password}' in mail.outbox[0].body)
self.assertTrue(f'{guest.mail}' in mail.outbox[0].body)
def test_is_member(self):
"""
Test that members are detected and not guests.
"""
organisator = Organisator(
name="Orga",
mail="orga@example.com"
)
organisator.save()
organisator_user = CustomUser(
username="orga",
email=organisator.mail
)
organisator_user.save()
reparateur = Reparateur(
name="Reparateur",
mail="repa@example.com"
)
reparateur.save()
reparateur_user = CustomUser(
username="repa",
email=reparateur.mail
)
reparateur_user.save()
guest = CustomUser.objects.get(pk=3)
self.assertTrue(is_member(organisator_user))
self.assertTrue(is_member(reparateur_user))
self.assertFalse(is_member(guest))
def test_create_one_time_login(self):
"""
Test one time login creation.
"""
user = CustomUser.objects.get(pk=3)
url = "/test/url/"
secret = create_one_time_login(user, url)
secret_hash = sha256(secret.encode('utf-8')).hexdigest()
otl = OneTimeLogin.objects.get(secret=secret_hash)
self.assertEqual(otl.url, url)

View file

@ -27,6 +27,7 @@ urlpatterns = [
path("member/login/",
views.member_login, name="member_login"),
path("cron", views.cron, name="cron"),
path("bootstrap", views.bootstrap, name="bootstrap"),
path("process_mails", views.process_mails, name="process_mails"),
]

View file

@ -26,6 +26,9 @@ from .forms import RegisterDevice, RegisterGuest
def send_one_time_login_mail(secret, mail, request):
"""
Send a mail with a one time login link.
"""
url = request.build_absolute_uri(
f'/onetimelogin/{secret}/')
subject = render_to_string(
@ -52,6 +55,9 @@ def send_one_time_login_mail(secret, mail, request):
def send_confirmation_mails(device, guest, cafe, request):
"""
Send confirmation mails for device registration.
"""
organizers = []
for organizer in Organisator.objects.all():
organizers.append(organizer.mail)
@ -110,6 +116,9 @@ def send_confirmation_mails(device, guest, cafe, request):
def send_guest_account_mail(guest, password, request):
"""
Send guest user account created mail.
"""
url = request.build_absolute_uri('/guest/profile/')
subject = render_to_string(
'repapp/mail/mail_new_guest_subject.html').replace('\n', '')
@ -139,6 +148,9 @@ def send_guest_account_mail(guest, password, request):
def is_member(user):
"""
Test is a user is a Repair-Café member.
"""
organisator = Organisator.objects.filter(mail=user.email).first()
reparateur = Reparateur.objects.filter(mail=user.email).first()
return organisator or reparateur
@ -146,7 +158,7 @@ def is_member(user):
def create_one_time_login(user, url) -> str:
"""
create_one_time_login create a one time login object for user logins.
create_one_time_login creates a one time login object for guest user logins.
"""
secret = sha256(
f'{user.email}{url}{datetime.datetime.now()}{random.randint(0,9999999)}'.encode(
@ -435,3 +447,13 @@ def one_time_login(request, secret: str):
else:
messages.add_message(request, messages.ERROR, 'Login fehlgeschlagen.')
return HttpResponseRedirect(reverse_lazy('index'))
def bootstrap(request):
"""
Bootstrap design test page.
"""
return render(
request,
"repapp/bootstrap.html"
)