feat: add tests
This commit is contained in:
6
later42/templates/about.html
Normal file
6
later42/templates/about.html
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% include 'about_include.html' %}
|
||||||
|
{% include 'faq_include.html' %}
|
||||||
|
{% endblock %}
|
24
later42/templates/about_include.html
Normal file
24
later42/templates/about_include.html
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<h2>Сервис для сохранения ссылок</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Сохраняйте заинтересовавшие вас ссылки для последующего вдумчивого чтения.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
В наше спешащее время мы не успеваем читать вдумчиво и спокойно. Пролистываем тонны информации за день.
|
||||||
|
Теперь можно не спешить, а просто отложить ссылку на интересную статью, чтобы потом вернуться к ней и в спокойной
|
||||||
|
обстановке вдумчиво ее прочитать.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Как это работает</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Для начала работы нужно только зарегистрироваться и получить API ключ.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Дальше устанавливаете Команду "Shortcut" в ваше iOS или MacOS устройство и через меню "Поделиться" добавляете ссылку.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
При первом запуске Команда спросит ваш API ключ. Дальше все будет работать прозрачно и без лишних вопросов.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p></p>
|
@ -29,6 +29,7 @@
|
|||||||
<div class="collapse navbar-collapse" id="navbarResponsive">
|
<div class="collapse navbar-collapse" id="navbarResponsive">
|
||||||
<ul class="navbar-nav ms-auto py-4 py-lg-0">
|
<ul class="navbar-nav ms-auto py-4 py-lg-0">
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
|
<li class="nav-item"><a class="nav-link px-lg-3 py-3 py-lg-4" href="{% url 'about' %}">О нас</a></li>
|
||||||
<li class="nav-item"><a class="nav-link px-lg-3 py-3 py-lg-4" href="{% url 'profile' %}">Профиль</a></li>
|
<li class="nav-item"><a class="nav-link px-lg-3 py-3 py-lg-4" href="{% url 'profile' %}">Профиль</a></li>
|
||||||
<li class="nav-item"><a class="nav-link px-lg-3 py-3 py-lg-4" href="{% url 'logout' %}">Выйти</a></li>
|
<li class="nav-item"><a class="nav-link px-lg-3 py-3 py-lg-4" href="{% url 'logout' %}">Выйти</a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
@ -77,7 +78,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-inline-item">
|
<li class="list-inline-item">
|
||||||
<a href="#!">
|
<a href="https://github.com/dntsk/later42">
|
||||||
<span class="fa-stack fa-lg">
|
<span class="fa-stack fa-lg">
|
||||||
<i class="fas fa-circle fa-stack-2x"></i>
|
<i class="fas fa-circle fa-stack-2x"></i>
|
||||||
<i class="fab fa-github fa-stack-1x fa-inverse"></i>
|
<i class="fab fa-github fa-stack-1x fa-inverse"></i>
|
||||||
@ -85,7 +86,8 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="small text-center text-muted fst-italic">© dntsk.dev, 2022</div>
|
<div class="small text-center text-muted fst-italic">Later 42 by <a href="https://dntsk.dev">dntsk.dev</a>, 2022</div>
|
||||||
|
<div class="small text-center text-muted fst-italic">v0.0.1</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
Test content block
|
|
||||||
<p></p>
|
|
9
later42/templates/faq_include.html
Normal file
9
later42/templates/faq_include.html
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{% load static %}
|
||||||
|
<h2>Часто задаваемые вопросы:</h2>
|
||||||
|
<p></p>
|
||||||
|
<h4>Где взять команду Shortcuts для iOS/MacOS?</h4>
|
||||||
|
<p><a href="{% static 'apps/later42.shortcut' %}">Скачать команду</a>.</p>
|
||||||
|
<p>После установки команды и первого запуска из меню "Поделиться" вас попросит ввести ваш API ключ. Взять его вы
|
||||||
|
можете в <a href="{% url 'profile' %}">вашем профиле</a>.</p>
|
||||||
|
<h4>Будет ли поддержка Android?</h4>
|
||||||
|
<p>Да, поддержка устройств на OS Android планируется через бота в Telegram. Сроки пока не известны.</p>
|
@ -25,6 +25,6 @@
|
|||||||
<h2 class="post-title">У вас нет сохраненных ссылок</h2>
|
<h2 class="post-title">У вас нет сохраненных ссылок</h2>
|
||||||
</div>
|
</div>
|
||||||
{% elif not user.is_authenticated %}
|
{% elif not user.is_authenticated %}
|
||||||
{% include 'docs.html' %}
|
{% include 'about_include.html' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -36,12 +36,12 @@
|
|||||||
{{ token }}
|
{{ token }}
|
||||||
<form action="{% url 'api_token' %}" method="post">
|
<form action="{% url 'api_token' %}" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="submit" value="Сбросить на новый">
|
<input class="btn btn-primary text-uppercase" type="submit" id="resetbutton" value="Сбросить на новый">
|
||||||
</form>
|
</form>
|
||||||
{% else %}
|
{% else %}
|
||||||
<form action="{% url 'api_token' %}" method="post">
|
<form action="{% url 'api_token' %}" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="submit" value="Создать">
|
<input class="btn btn-primary text-uppercase" type="submit" id="createbutton" value="Создать">
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
0
later42/tests/__init__.py
Normal file
0
later42/tests/__init__.py
Normal file
0
later42/tests/views/__init__.py
Normal file
0
later42/tests/views/__init__.py
Normal file
36
later42/tests/views/test_api.py
Normal file
36
later42/tests/views/test_api.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.test import TestCase, Client
|
||||||
|
from django.urls import reverse
|
||||||
|
from rest_framework.authtoken.models import Token
|
||||||
|
from rest_framework.test import APIClient
|
||||||
|
|
||||||
|
from later42.models.urls import URL
|
||||||
|
|
||||||
|
|
||||||
|
class ApiTests(TestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
|
self.username = 'testuser1'
|
||||||
|
self.email = 'testuser1@email.com'
|
||||||
|
self.password = 'password1234567QWERTY'
|
||||||
|
self.user = User.objects.create(
|
||||||
|
username=self.username,
|
||||||
|
email=self.email
|
||||||
|
)
|
||||||
|
self.user.set_password(self.password)
|
||||||
|
self.user.save()
|
||||||
|
|
||||||
|
self.url = 'https://google.com'
|
||||||
|
|
||||||
|
self.c = Client()
|
||||||
|
self.c.login(username=self.username, password=self.password)
|
||||||
|
self.assertTrue(self.user.is_authenticated)
|
||||||
|
self.token = Token.objects.create(user=self.user)
|
||||||
|
|
||||||
|
def test_url_create(self):
|
||||||
|
token = Token.objects.get(user=self.user)
|
||||||
|
client = APIClient()
|
||||||
|
client.credentials(HTTP_AUTHORIZATION='Token ' + token.key)
|
||||||
|
response = client.post(reverse('urls') + f'?url={self.url}')
|
||||||
|
assert response.status_code == 200
|
||||||
|
self.assertContains(response, 'success')
|
||||||
|
self.assertEqual(URL.objects.count(), 1)
|
36
later42/tests/views/test_api_token.py
Normal file
36
later42/tests/views/test_api_token.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.test import TestCase, Client
|
||||||
|
from django.urls import reverse
|
||||||
|
from rest_framework.authtoken.models import Token
|
||||||
|
|
||||||
|
|
||||||
|
class ApiKeyTests(TestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
|
self.username = 'testuser1'
|
||||||
|
self.email = 'testuser1@email.com'
|
||||||
|
self.password = 'password1234567QWERTY'
|
||||||
|
self.user = User.objects.create(
|
||||||
|
username=self.username,
|
||||||
|
email=self.email
|
||||||
|
)
|
||||||
|
self.user.set_password(self.password)
|
||||||
|
self.user.save()
|
||||||
|
|
||||||
|
self.c = Client()
|
||||||
|
self.c.login(username=self.username, password=self.password)
|
||||||
|
self.assertTrue(self.user.is_authenticated)
|
||||||
|
self.response = self.c.get(reverse('api_token'))
|
||||||
|
|
||||||
|
def test_api_key_creation(self):
|
||||||
|
assert self.response.status_code == 302
|
||||||
|
assert self.response.url == '/profile/'
|
||||||
|
|
||||||
|
def test_api_key_created(self):
|
||||||
|
token = Token.objects.get(user=self.user)
|
||||||
|
assert token is not None
|
||||||
|
|
||||||
|
def test_api_key_reset(self):
|
||||||
|
token_old = Token.objects.get(user=self.user)
|
||||||
|
self.response = self.c.get(reverse('api_token'))
|
||||||
|
token_new = Token.objects.get(user=self.user)
|
||||||
|
assert token_old != token_new
|
33
later42/tests/views/test_index.py
Normal file
33
later42/tests/views/test_index.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.test import TestCase, Client
|
||||||
|
from django.urls import reverse
|
||||||
|
from rest_framework.authtoken.models import Token
|
||||||
|
|
||||||
|
from later42.models.urls import URL
|
||||||
|
|
||||||
|
|
||||||
|
class IndexTests(TestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
|
self.username = 'testuser1'
|
||||||
|
self.email = 'testuser1@email.com'
|
||||||
|
self.password = 'password1234567QWERTY'
|
||||||
|
self.user = User.objects.create(
|
||||||
|
username=self.username,
|
||||||
|
email=self.email
|
||||||
|
)
|
||||||
|
self.user.set_password(self.password)
|
||||||
|
self.user.save()
|
||||||
|
|
||||||
|
self.c = Client()
|
||||||
|
self.c.login(username=self.username, password=self.password)
|
||||||
|
self.assertTrue(self.user.is_authenticated)
|
||||||
|
|
||||||
|
def test_url_delete(self):
|
||||||
|
url = URL.objects.create(
|
||||||
|
url='https://www.google.com/',
|
||||||
|
user=self.user
|
||||||
|
)
|
||||||
|
url.save()
|
||||||
|
self.c.delete(reverse('delete', kwargs={'url_id': url.id}))
|
||||||
|
url = URL.objects.filter(id=url.id)
|
||||||
|
assert len(url) == 0
|
41
later42/tests/views/test_login.py
Normal file
41
later42/tests/views/test_login.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.test import TestCase, Client
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
|
||||||
|
class LoginPageTests(TestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
|
self.username = 'testuser1'
|
||||||
|
self.email = 'testuser1@email.com'
|
||||||
|
self.password = 'password1234567QWERTY'
|
||||||
|
self.user = User.objects.create(
|
||||||
|
username=self.username,
|
||||||
|
email=self.email
|
||||||
|
)
|
||||||
|
self.user.set_password(self.password)
|
||||||
|
self.user.save()
|
||||||
|
|
||||||
|
self.c = Client()
|
||||||
|
self.c.login(username=self.username, password=self.password)
|
||||||
|
self.assertTrue(self.user.is_authenticated)
|
||||||
|
|
||||||
|
def test_login_page(self):
|
||||||
|
response = self.client.get(reverse('login'))
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
def test_index_page_after_login(self):
|
||||||
|
response = self.c.get(reverse('index'))
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.context['user'].is_authenticated
|
||||||
|
self.assertTemplateUsed(response, 'index.html')
|
||||||
|
self.assertContains(response, '/accounts/logout/')
|
||||||
|
|
||||||
|
def test_profile_page_template(self):
|
||||||
|
response = self.c.get(reverse('profile'))
|
||||||
|
assert response.status_code == 200
|
||||||
|
self.assertTemplateUsed(response, 'profile.html')
|
||||||
|
|
||||||
|
def test_api_key_creation_button(self):
|
||||||
|
response = self.c.get(reverse('profile'))
|
||||||
|
assert response.status_code == 200
|
||||||
|
self.assertContains(response, 'id="createbutton"')
|
19
later42/tests/views/test_pages.py
Normal file
19
later42/tests/views/test_pages.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
|
||||||
|
class PageTests(TestCase):
|
||||||
|
def test_index_page_url(self):
|
||||||
|
response = self.client.get(reverse('index'))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertTemplateUsed(response, template_name='index.html')
|
||||||
|
|
||||||
|
def test_about_page_url(self):
|
||||||
|
response = self.client.get(reverse('about'))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertTemplateUsed(response, template_name='about.html')
|
||||||
|
|
||||||
|
def test_profile_page_url_redirect(self):
|
||||||
|
response = self.client.get(reverse('profile'))
|
||||||
|
self.assertEqual(response.status_code, 302)
|
33
later42/tests/views/test_signup.py
Normal file
33
later42/tests/views/test_signup.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
|
||||||
|
class SignUpPageTests(TestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
|
self.username = 'testuser'
|
||||||
|
self.email = 'testuser@email.com'
|
||||||
|
self.password = 'password1234567QWERTY'
|
||||||
|
|
||||||
|
def test_signup_page_url(self):
|
||||||
|
response = self.client.get(reverse('signup'))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertTemplateUsed(response, template_name='signup.html')
|
||||||
|
|
||||||
|
def test_signup_page_view_name(self):
|
||||||
|
response = self.client.get(reverse('signup'))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertTemplateUsed(response, template_name='signup.html')
|
||||||
|
|
||||||
|
def test_signup_form(self):
|
||||||
|
response = self.client.post(reverse('signup'), data={
|
||||||
|
'username': self.username,
|
||||||
|
'email': self.email,
|
||||||
|
'password1': self.password,
|
||||||
|
'password2': self.password
|
||||||
|
})
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
|
||||||
|
users = get_user_model().objects.all()
|
||||||
|
self.assertEqual(users.count(), 1)
|
@ -22,7 +22,7 @@ from django.contrib.auth.views import LoginView
|
|||||||
from rest_framework import routers, serializers, viewsets
|
from rest_framework import routers, serializers, viewsets
|
||||||
|
|
||||||
from later42.forms import CustomLoginForm
|
from later42.forms import CustomLoginForm
|
||||||
from later42.views import index, profile, api, api_token, signup
|
from later42.views import index, profile, api, api_token, signup, about
|
||||||
|
|
||||||
|
|
||||||
class UserSerializer(serializers.HyperlinkedModelSerializer):
|
class UserSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
@ -45,9 +45,10 @@ urlpatterns = [
|
|||||||
path("accounts/login/", LoginView.as_view(authentication_form=CustomLoginForm), name="login"),
|
path("accounts/login/", LoginView.as_view(authentication_form=CustomLoginForm), name="login"),
|
||||||
path('accounts/', include('django_registration.backends.activation.urls')),
|
path('accounts/', include('django_registration.backends.activation.urls')),
|
||||||
path("accounts/", include("django.contrib.auth.urls")),
|
path("accounts/", include("django.contrib.auth.urls")),
|
||||||
path('api/url/', api.URL.as_view(), name='urls'),
|
|
||||||
path('', index.get, name='index'),
|
|
||||||
path('delete/<int:url_id>', index.delete, name='delete'),
|
|
||||||
path('profile/', profile.get, name='profile'),
|
path('profile/', profile.get, name='profile'),
|
||||||
|
path('api/url/', api.URL.as_view(), name='urls'),
|
||||||
|
path('delete/<int:url_id>', index.delete, name='delete'),
|
||||||
path('api_token/', api_token.create, name='api_token'),
|
path('api_token/', api_token.create, name='api_token'),
|
||||||
|
path('', index.get, name='index'),
|
||||||
|
path('about/', about.get, name='about'),
|
||||||
]
|
]
|
||||||
|
5
later42/views/about.py
Normal file
5
later42/views/about.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
|
||||||
|
def get(request):
|
||||||
|
return render(request, 'about.html')
|
@ -1,13 +1,13 @@
|
|||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from rest_framework.authtoken.models import Token
|
from rest_framework.authtoken.models import Token
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
def create(request):
|
def create(request):
|
||||||
token = Token.objects.filter(user=request.user)
|
token = Token.objects.filter(user=request.user)
|
||||||
try:
|
if len(token) > 0:
|
||||||
token.delete()
|
token.delete()
|
||||||
except:
|
|
||||||
pass
|
|
||||||
token = Token.objects.create(user=request.user)
|
token = Token.objects.create(user=request.user)
|
||||||
token.save()
|
token.save()
|
||||||
return redirect('profile')
|
return redirect('profile')
|
Reference in New Issue
Block a user