22 Commits

Author SHA1 Message Date
06c7b2ea52 Add the Response model
This is to store RSVPs.
2018-07-12 12:22:49 +02:00
3308be40ee Display the locked status of profiles on the profile details page 2018-07-12 12:18:51 +02:00
9b01431641 Make it possible for users to lock their profiles on the profile editing page 2018-07-12 12:17:18 +02:00
2b1378310a [Bugfix] Make the profile editing form redirect to the profile editing form after saving
As funny as it says, this provides better UX (and the same technique is used on the other forms.
2018-07-12 12:16:22 +02:00
5639c3f578 Make it possible to accept follow requests 2018-07-12 12:10:53 +02:00
61f10f951c Make it possible to lock profiles
Locked profiles cannot be followed
2018-07-12 12:10:53 +02:00
496b5b6c04 [Refactor] Move notification creation to the Profile model 2018-07-12 12:00:36 +02:00
dc0b2954c1 [Refactor] Move invitation code to the Event model 2018-07-12 11:08:13 +02:00
36c2f0fd77 Add test for following a user through the web UI 2018-07-12 10:34:59 +02:00
27c78ff36f Create the Profile.follow() method 2018-07-12 10:34:59 +02:00
37e08fed22 Add tests for logging in 2018-07-12 10:34:57 +02:00
a0fba3f2af Move registration related tests to a separate file 2018-07-12 10:32:16 +02:00
48a19a2296 Update tests
Move the `client` fixture to the helpers module, as it will be used by many other test cases.

Add the AGPL header to the test files.
2018-07-12 10:28:59 +02:00
5d886a7853 [Bugfix] Fix the FQN of invalid remote profiles (ie. no domain set) 2018-07-12 10:17:56 +02:00
5550e5ecf3 Make PyLint happy again 2018-07-12 10:16:04 +02:00
0a3cfafef3 Fix .gitignore rule for .mo files 2018-07-12 09:13:24 +02:00
8e3bcd8ede Create a view that can list all events 2018-07-11 12:57:32 +02:00
48ffb0d472 [Refactor] Refactor the month view template so it can display all events
…not just the events of the current user.
2018-07-11 12:57:32 +02:00
c3348d3212 Make it possible to edit one’s profile
…even though it’s only one field yet.
2018-07-11 12:57:32 +02:00
1a69928241 [Refactor] Create a base settings page
This will make it easier to add new settings pages for e.g. privacy settings, applications,
etc. later.
2018-07-11 12:57:32 +02:00
7b935afdad [Bugfix] Fix the event creating code
It tried to associate the event with a user instead of a profile.  It is a regression introduced
by #41
2018-07-11 12:57:32 +02:00
303dd3d082 [Bugfix] Fix the display name on the profile pages 2018-07-11 09:35:32 +02:00
15 changed files with 650 additions and 141 deletions

2
.gitignore vendored
View File

@@ -1,5 +1,5 @@
__pycache__/
/calsocial/local.db
/messages.pot
/app/translations/*/LC_MESSAGES/*.mo
/calsocial/translations/*/LC_MESSAGES/*.mo
/.pytest_cache/

View File

@@ -170,7 +170,7 @@ class CalendarSocialApp(Flask):
calendar = GregorianCalendar(timestamp.timestamp())
return render_template('index.html', calendar=calendar)
return render_template('index.html', calendar=calendar, user_only=True)
@staticmethod
@route('/register', methods=['POST', 'GET'])
@@ -217,7 +217,7 @@ class CalendarSocialApp(Flask):
form = EventForm()
if form.validate_on_submit():
event = Event(user=current_user)
event = Event(profile=current_user.profile)
form.populate_obj(event)
db.session.add(event)
@@ -255,7 +255,7 @@ class CalendarSocialApp(Flask):
"""
from .forms import InviteForm
from .models import db, Event, Invitation, Notification, NotificationAction
from .models import db, Event
try:
event = Event.query.filter(Event.event_uuid == event_uuid).one()
@@ -267,15 +267,7 @@ class CalendarSocialApp(Flask):
form = InviteForm(event)
if form.validate_on_submit():
invite = Invitation(event=event, sender=current_user.profile)
form.populate_obj(invite)
db.session.add(invite)
notification = Notification(profile=form.invitee.data,
actor=current_user.profile,
item=event,
action=NotificationAction.invite)
db.session.add(notification)
event.invite(current_user.profile, invitee=form.invitee.data)
db.session.commit()
@@ -300,11 +292,12 @@ class CalendarSocialApp(Flask):
@staticmethod
@route('/profile/@<string:username>/follow')
@login_required
def follow_user(username):
"""View for following a user
"""
from .models import db, Profile, User, UserFollow, Notification, NotificationAction
from .models import db, Profile, User
try:
profile = Profile.query.join(User).filter(User.username == username).one()
@@ -312,16 +305,7 @@ class CalendarSocialApp(Flask):
abort(404)
if profile.user != current_user:
follow = UserFollow(follower=current_user.profile,
followed=profile,
accepted_at=datetime.utcnow())
db.session.add(follow)
notification = Notification(profile=profile,
actor=current_user.profile,
item=profile,
action=NotificationAction.follow)
db.session.add(notification)
profile.follow(follower=current_user.profile)
db.session.commit()
@@ -399,5 +383,83 @@ class CalendarSocialApp(Flask):
return render_template('first-steps.html', form=form)
@staticmethod
@route('/edit-profile', methods=['GET', 'POST'])
@login_required
def edit_profile():
"""View for editing ones profile
"""
from .forms import ProfileForm
from .models import db
form = ProfileForm(current_user.profile)
if form.validate_on_submit():
form.populate_obj(current_user.profile)
db.session.add(current_user.profile)
db.session.commit()
return redirect(url_for('edit_profile'))
return render_template('profile-edit.html', form=form)
@staticmethod
@route('/all-events')
def all_events():
"""View for listing all available events
"""
from .calendar_system.gregorian import GregorianCalendar
try:
timestamp = datetime.fromtimestamp(float(request.args.get('date')))
except TypeError:
timestamp = datetime.utcnow()
calendar = GregorianCalendar(timestamp.timestamp())
return render_template('index.html', calendar=calendar, user_only=False)
@staticmethod
@route('/follow-requests')
@login_required
def follow_requests():
"""View for listing follow requests
"""
from .models import UserFollow
requests = UserFollow.query \
.filter(UserFollow.followed == current_user.profile) \
.filter(UserFollow.accepted_at.is_(None))
return render_template('follow-requests.html', requests=requests)
@staticmethod
@route('/follow-request/<int:follower_id>/accept')
@login_required
def accept_follow(follower_id):
"""View for accepting a follow request
"""
from .models import db, UserFollow
try:
req = UserFollow.query \
.filter(UserFollow.followed == current_user.profile) \
.filter(UserFollow.follower_id == follower_id) \
.one()
except NoResultFound:
abort(404)
if req.accepted_at is None:
req.accept()
db.session.add(req)
db.session.commit()
return redirect(url_for('follow_requests'))
app = CalendarSocialApp(__name__)

View File

@@ -330,8 +330,24 @@ class FirstStepsForm(FlaskForm):
display_name = StringField(
label=_('Display name'),
validators=[DataRequired()],
# pylint: disable=line-too-long
description=_('This will be shown to other users as your name. You can use your real name, or any nickname you like.'))
time_zone = TimezoneField(
label=_('Your time zone'),
validators=[DataRequired()],
description=_('The start and end times of events will be displayed in this time zone.'))
class ProfileForm(FlaskForm):
"""Form for editing a user profile
"""
display_name = StringField(label=_('Display name'), validators=[DataRequired()])
locked = BooleanField(label=_('Lock profile'))
def __init__(self, profile, *args, **kwargs):
kwargs.update({'display_name': profile.display_name})
kwargs.update({'locked': profile.locked})
FlaskForm.__init__(self, *args, **kwargs)
self.profile = profile

View File

@@ -103,6 +103,24 @@ class ResponseType(Enum):
return Enum.__eq__(self, other)
class EventAvailability(Enum):
free = 0
busy = 1
class UserAvailability(EventAvailability):
tentative = 2
class ResponseVisibility(Enum):
private = 0
organisers = 1
attendees = 2
followers = 3
friends = 4
public = 5
class SettingsProxy:
"""Proxy object to get settings for a user
"""
@@ -246,6 +264,9 @@ class Profile(db.Model): # pylint: disable=too-few-public-methods
#: The display name
display_name = db.Column(db.Unicode(length=80), nullable=False)
#: If locked, a profile cannot be followed without the owners consent
locked = db.Column(db.Boolean(), default=False)
@property
def fqn(self):
"""The fully qualified name of the profile
@@ -265,7 +286,7 @@ class Profile(db.Model): # pylint: disable=too-few-public-methods
domain = ''
else:
username = self.username
domain = '@' + self.domain
domain = f'@{self.domain}'
return f'<Profile {self.id}(@{username}{domain})>'
@@ -290,7 +311,8 @@ class Profile(db.Model): # pylint: disable=too-few-public-methods
return Profile.query \
.join(UserFollow.followed) \
.filter(UserFollow.follower == self)
.filter(UserFollow.follower == self) \
.filter(UserFollow.accepted_at.isnot(None))
@property
def follower_list(self):
@@ -303,7 +325,8 @@ class Profile(db.Model): # pylint: disable=too-few-public-methods
return Profile.query \
.join(UserFollow.follower) \
.filter(UserFollow.followed == self)
.filter(UserFollow.followed == self) \
.filter(UserFollow.accepted_at.isnot(None))
@property
def url(self):
@@ -317,6 +340,48 @@ class Profile(db.Model): # pylint: disable=too-few-public-methods
return NotImplemented
def follow(self, follower):
"""Make ``follower`` follow this profile
"""
if not isinstance(follower, Profile):
raise TypeError('Folloer must be a Profile object')
timestamp = None if self.locked else datetime.utcnow()
user_follow = UserFollow(follower=follower, followed=self, accepted_at=timestamp)
db.session.add(user_follow)
notification = self.notify(follower, self, NotificationAction.follow)
db.session.add(notification)
return user_follow
def notify(self, actor, item, action):
"""Notify this profile about ``action`` on ``item`` by ``actor``
:param actor: the actor who generated the notification
:type actor: Profile
:param item: the item ``action`` was performed on
:type item: any
:param action: the type of the action
:type action: NotificationAction, str
:raises TypeError: if ``actor`` is not a `Profile` object
:returns: the generated notification. It is already added to the database session, but
not committed
:rtype: Notification
"""
if not isinstance(actor, Profile):
raise TypeError('actor must be a Profile instance')
if isinstance(action, str):
action = NotificationAction[action]
notification = Notification(profile=self, actor=actor, item=item, action=action)
return notification
class Event(db.Model):
"""Database model for events
@@ -400,6 +465,20 @@ class Event(db.Model):
return url_for('event_details', event_uuid=self.event_uuid)
def invite(self, inviter, invited):
"""Invite ``invited`` to the event
The invitation will arrive from ``inviter``.
"""
invite = Invitation(event=self, sender=inviter, invitee=invited)
db.session.add(invite)
notification = invited.notify(inviter, self, NotificationAction.invite)
db.session.add(notification)
return invite
class UserSetting(db.Model): # pylint: disable=too-few-public-methods
"""Database model for user settings
@@ -543,6 +622,12 @@ class UserFollow(db.Model): # pylint: disable=too-few-public-methods
#: The timestamp when the follow was accepted
accepted_at = db.Column(db.DateTime(), nullable=True)
def accept(self):
"""Accept this follow request
"""
self.accepted_at = datetime.utcnow()
class Notification(db.Model):
"""Database model for notifications

View File

@@ -0,0 +1,13 @@
{% extends 'base.html' %}
{% block content %}
<h2>{% trans %}Follow requests{% endtrans %}</h2>
<ul>
{% for req in requests %}
<li>
{{ req.follower }}
<a href="{{ url_for('accept_follow', follower_id=req.follower_id) }}">{% trans %}Accept{% endtrans %}</a>
</li>
{% endfor %}
</ul>
{% endblock content %}

View File

@@ -120,7 +120,7 @@
{%- endif %}
<td class="{% if day.month != calendar.timestamp.month %} other-month{% endif %}{% if day.date() == now.date() %} today{% endif %}">
<span class="day-num">{{ day.day }}</span>
{% for event in calendar.day_events(day, user=current_user) %}
{% for event in calendar.day_events(day, user=current_user if user_only else none) %}
<a href="{{ url_for('event_details', event_uuid=event.event_uuid) }}" class="event">
{{ event.start_time_for_user(current_user).strftime('%H:%M') }}{{ event.end_time_for_user(current_user).strftime('%H:%M') }}
{{ event.title }}

View File

@@ -2,7 +2,10 @@
{% block content %}
<h1>
{{ profile.name }}
{% if profile.locked %}
[locked]
{% endif %}
{{ profile.display_name }}
<small>@{{ profile.user.username}}</small>
</h1>
{% if profile.user != current_user %}

View File

@@ -0,0 +1,22 @@
{% extends 'settings-base.html' %}
{% block content %}
{{ super() }}
<h2>{% trans %}Edit profile{% endtrans %}</h2>
<form method="post">
{{ form.hidden_tag() }}
{{ form.errors }}
{{ form.display_name.errors }}
{{ form.display_name.label }}
{{ form.display_name }}
<br>
{{ form.locked.errors }}
{{ form.locked.label }}
{{ form.locked}}
<br>
<button type="submit">{% trans %}Save{% endtrans %}</button>
</form>
{% endblock content %}

View File

@@ -0,0 +1,10 @@
{% extends 'base.html' %}
{% block content %}
<nav>
<ul>
<li><a href="{{ url_for('edit_profile') }}">{% trans %}Edit profile{% endtrans %}</a></li>
<li><a href="{{ url_for('settings') }}">{% trans %}Settings{% endtrans %}</a></li>
</ul>
</nav>
{% endblock %}

View File

@@ -1,6 +1,8 @@
{% extends 'base.html' %}
{% extends 'settings-base.html' %}
{% block content %}
{{ super() }}
<h2>{% trans %}Settings{% endtrans %}</h2>
<form method="post">
{{ form.hidden_tag() }}

72
tests/helpers.py Normal file
View File

@@ -0,0 +1,72 @@
# Calendar.social
# Copyright (C) 2018 Gergely Polonkai
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""Helper functions and fixtures for testing
"""
import pytest
import calsocial
from calsocial.models import db
def configure_app():
"""Set default configuration values for testing
"""
calsocial.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///'
calsocial.app.config['TESTING'] = True
calsocial.app.config['WTF_CSRF_ENABLED'] = False
@pytest.fixture
def client():
"""Fixture that provides a Flask test client
"""
configure_app()
client = calsocial.app.test_client()
with calsocial.app.app_context():
db.create_all()
yield client
with calsocial.app.app_context():
db.drop_all()
def login(client, username, password, no_redirect=False):
"""Login with the specified username and password
"""
return client.post('/login',
data={'email': username, 'password': password},
follow_redirects=not no_redirect)
@pytest.fixture
def database():
"""Fixture to provide all database tables in an active application context
"""
configure_app()
with calsocial.app.app_context():
db.create_all()
yield db
db.drop_all()

View File

@@ -1,23 +1,23 @@
import pytest
# Calendar.social
# Copyright (C) 2018 Gergely Polonkai
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import calsocial
from calsocial.models import db, User
"""General tests for Calendar.social
"""
@pytest.fixture
def client():
calsocial.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///'
calsocial.app.config['TESTING'] = True
calsocial.app.config['WTF_CSRF_ENABLED'] = False
client = calsocial.app.test_client()
with calsocial.app.app_context():
db.create_all()
yield client
with calsocial.app.app_context():
db.drop_all()
from helpers import client
def test_index_no_login(client):
@@ -26,96 +26,3 @@ def test_index_no_login(client):
page = client.get('/')
assert b'Welcome to Calendar.social' in page.data
def test_register_page(client):
"""Test the registration page
"""
page = client.get('/register')
assert b'Register</button>' in page.data
def test_register_post_empty(client):
"""Test sending empty registration data
"""
page = client.post('/register', data={})
assert b'This field is required' in page.data
def test_register_invalid_email(client):
"""Test sending an invalid email address
"""
page = client.post('/register', data={
'username': 'test',
'email': 'test',
'password': 'password',
'password_retype': 'password',
})
assert b'Invalid email address' in page.data
def test_register_password_mismatch(client):
"""Test sending different password for registration
"""
page = client.post('/register', data={
'username': 'test',
'email': 'test@example.com',
'password': 'password',
'password_retype': 'something',
})
assert b'The two passwords must match' in page.data
def test_register(client):
"""Test user registration
"""
page = client.post('/register', data={
'username': 'test',
'email': 'test@example.com',
'password': 'password',
'password_retype': 'password',
})
print(page.data)
assert page.status_code == 302
assert page.location == 'http://localhost/'
with calsocial.app.app_context():
user = User.query.one()
assert user.username == 'test'
assert user.email == 'test@example.com'
def test_register_existing_username(client):
"""Test registering an existing username
"""
with calsocial.app.app_context():
user = User(username='test', email='test@example.com')
db.session.add(user)
db.session.commit()
page = client.post('/register', data={
'username': 'test',
'email': 'test2@example.com',
'password': 'password',
'password_retype': 'password',
})
assert b'This username is not available' in page.data
def test_register_existing_email(client):
"""Test registering an existing email address
"""
with calsocial.app.app_context():
user = User(username='test', email='test@example.com')
db.session.add(user)
db.session.commit()
page = client.post('/register', data={
'username': 'tester',
'email': 'test@example.com',
'password': 'password',
'password_retype': 'password',
})
assert b'This email address can not be used' in page.data

117
tests/test_follow.py Normal file
View File

@@ -0,0 +1,117 @@
# Calendar.social
# Copyright (C) 2018 Gergely Polonkai
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""Profile related tests for Calendar.social
"""
import calsocial
from calsocial.models import db, Notification, NotificationAction, Profile, User, UserFollow
from helpers import client, database, login
def test_profile_follow(database):
"""Test the Profile.follow() method
"""
follower_user = User(username='follower',
email='follower@example.com',
password='passworder',
active=True)
followed_user = User(username='followed', email='followed@example.com')
follower = Profile(display_name='Follower', user=follower_user)
followed = Profile(display_name='Followed', user=followed_user)
db.session.add_all([follower, followed])
db.session.commit()
user_follow = followed.follow(follower=follower)
db.session.commit()
# The new follower record should have the fields set correctly
assert user_follow.followed == followed
assert user_follow.follower == follower
# There should be a notification about the follow
notification = Notification.query.one()
assert notification.actor == follower
assert notification.item == followed
assert notification.action == NotificationAction.follow
assert follower in followed.follower_list
assert followed in follower.followed_list
def test_follow_ui(client):
"""Test following on the web interface
"""
with calsocial.app.app_context():
follower_user = User(username='follower',
email='follower@example.com',
password='passworder',
active=True)
followed_user = User(username='followed', email='followed@example.com')
follower = Profile(display_name='Follower', user=follower_user)
followed = Profile(display_name='Followed', user=followed_user)
db.session.add_all([follower, followed])
db.session.commit()
login(client, 'follower', 'passworder')
page = client.get('/profile/@followed/follow')
assert page.status_code == 302
assert page.location == 'http://localhost/profile/%40followed'
with calsocial.app.app_context():
db.session.add_all([follower, followed])
follow = UserFollow.query.one()
assert follow.follower == follower
assert follow.followed == followed
assert follow.accepted_at is not None
def test_locked_profile(database):
"""Test following a locked profile
"""
follower_user = User(username='follower',
email='follower@example.com',
password='passworder',
active=True)
followed_user = User(username='followed', email='followed@example.com')
follower = Profile(display_name='Follower', user=follower_user)
followed = Profile(display_name='Followed', user=followed_user, locked=True)
db.session.add_all([follower, followed])
db.session.commit()
user_follow = followed.follow(follower=follower)
db.session.commit()
# The new follower record should have the fields set correctly
assert user_follow.followed == followed
assert user_follow.follower == follower
assert not user_follow.accepted_at
# There should be a notification about the follow
notification = Notification.query.one()
assert notification.actor == follower
assert notification.item == followed
assert notification.action == NotificationAction.follow
assert follower not in followed.follower_list
assert followed not in follower.followed_list

84
tests/test_login.py Normal file
View File

@@ -0,0 +1,84 @@
# Calendar.social
# Copyright (C) 2018 Gergely Polonkai
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""General tests for Calendar.social
"""
import calsocial
from calsocial.models import db, User
from helpers import client, login
def test_index_no_login(client):
"""Test the main page without logging in
"""
page = client.get('/')
assert b'Welcome to Calendar.social' in page.data
def test_login_invalid_user(client):
"""Test logging in with a non-existing user
"""
page = login(client, 'username', 'password')
assert b'Specified user does not exist' in page.data
def test_login_bad_password(client):
"""Test logging in with a bad password
"""
with calsocial.app.app_context():
user = User(username='test', email='test@example.com', password='password')
db.session.add(user)
db.session.commit()
page = login(client, 'test', password='else')
assert b'Invalid password' in page.data
def test_login_email(client):
"""Test logging in with the email address instead of the username
"""
with calsocial.app.app_context():
user = User(username='test', email='test@example.com', password='password', active=True)
db.session.add(user)
db.session.commit()
page = login(client, 'test@example.com', password='password')
assert b'Logged in as ' in page.data
def test_login_first_steps(client):
"""Test logging in with a new user
They must be redirected to the first login page
"""
with calsocial.app.app_context():
user = User(username='test', email='test@example.com', password='password', active=True)
db.session.add(user)
db.session.commit()
page = login(client, 'test', password='password', no_redirect=True)
# First, we must be redirected to the main page
assert page.location == 'http://localhost/'
page = client.get('/')
assert page.location == 'http://localhost/first-steps'

116
tests/test_register.py Normal file
View File

@@ -0,0 +1,116 @@
# Calendar.social
# Copyright (C) 2018 Gergely Polonkai
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""General tests for Calendar.social
"""
import calsocial
from calsocial.models import db, User
from helpers import client
def test_register_page(client):
"""Test the registration page
"""
page = client.get('/register')
assert b'Register</button>' in page.data
def test_register_post_empty(client):
"""Test sending empty registration data
"""
page = client.post('/register', data={})
assert b'This field is required' in page.data
def test_register_invalid_email(client):
"""Test sending an invalid email address
"""
page = client.post('/register', data={
'username': 'test',
'email': 'test',
'password': 'password',
'password_retype': 'password',
})
assert b'Invalid email address' in page.data
def test_register_password_mismatch(client):
"""Test sending different password for registration
"""
page = client.post('/register', data={
'username': 'test',
'email': 'test@example.com',
'password': 'password',
'password_retype': 'something',
})
assert b'The two passwords must match' in page.data
def test_register(client):
"""Test user registration
"""
page = client.post('/register', data={
'username': 'test',
'email': 'test@example.com',
'password': 'password',
'password_retype': 'password',
})
print(page.data)
assert page.status_code == 302
assert page.location == 'http://localhost/'
with calsocial.app.app_context():
user = User.query.one()
assert user.username == 'test'
assert user.email == 'test@example.com'
def test_register_existing_username(client):
"""Test registering an existing username
"""
with calsocial.app.app_context():
user = User(username='test', email='test@example.com')
db.session.add(user)
db.session.commit()
page = client.post('/register', data={
'username': 'test',
'email': 'test2@example.com',
'password': 'password',
'password_retype': 'password',
})
assert b'This username is not available' in page.data
def test_register_existing_email(client):
"""Test registering an existing email address
"""
with calsocial.app.app_context():
user = User(username='test', email='test@example.com')
db.session.add(user)
db.session.commit()
page = client.post('/register', data={
'username': 'tester',
'email': 'test@example.com',
'password': 'password',
'password_retype': 'password',
})
assert b'This email address can not be used' in page.data