Commit 32e6d9f3 authored by MariaV's avatar MariaV
Browse files

Merge branch 'even_more_tests' into 'master'

Added test for moderator view with 'Account Approver' user and refined user_landing template. Added PendingNoteDetailView with template.

See merge request !50
parents 15e49a3a 0c1b2e15
{% extends 'shared/layout.html' %}
{% load static %}
<!-- Load custom filters from shared app, including ISO 8901 Date-Time Converter -->
{% load custom_filters %}
<!-- Load markdownify -->
{% load markdownify %}
<!-- Extra links block for pages that have a user_identifier piped to them.-->
{% block extra_links %}
{% include 'shared/extend_sidebar.html' %}
{% endblock %}
<!-- Start Page Render -->
{% block h3 %}
</h3>
<div class="row justify-content-between">
<p class="col-auto mb-0 small pb-0 pt-0">{{note.linked_project}}</p>
<p class="col-auto text-muted small mb-0 pb-0 pt-0 text-right">Created by {{note.linked_user.user_identifier}}</p>
</div>
<hr class="mb-0 mt-0">
<div class="row justify-content-between">
<div class="col-auto"><p class="mb-0 small pb-0 pt-0">Milestone: NA, Note Pending Mod Approval.</p></div>
<div class="col-auto">
<p class="text-muted small mb-0 pb-0 pt-0 text-right">
Labels: NA, Note Pending Mod Approval</p>
</div>
</div>
<div class="row mt-5">
<div class="col-12">
<h4><span class="badge badge-primary mr-2">Pending Note</span>on Issue: "{{note.gitlab_issue_title|capfirst|truncatewords:20}}"</h4>
</div>
<div class="col-12 small mt-0 mb-3 text-right">
</div>
</div>
{% endblock %}
{% block subheader %}
<p class="form-control bg-light">"{{note.body|capfirst|markdownify}}"</p>
<p class="small text-muted ml-3">(***This note is pending moderator approval.)</p>
{% endblock %}
{% block content %}
<div class="row">
<div class="col-12">
<!-- Generate a link to go back to the user's landing page -->
{% url 'user-landing' note.linked_user.user_identifier as go_back_url %}
<p>
<a href="{{go_back_url}}">Go Back To Landing Page</a>
</p>
</div>
</div>
{% endblock %}
\ No newline at end of file
...@@ -46,11 +46,15 @@ ...@@ -46,11 +46,15 @@
<p class='mt-4 mb-0'> <p class='mt-4 mb-0'>
<ul> <ul>
<li>{% url 'project-list' results.user_identifier as project_list_url %} <li>{% url 'project-list' results.user_identifier as project_list_url %}
See a list of <a href="{{project_list_url}}">all projects</a> using this portal.</li> <a href="{{project_list_url}}">See a list all projects</a> using this portal. Each project links
to a detail page with a list of issues.</li>
<li>{% url 'issue-search' results.user_identifier as search_url %} <li>{% url 'issue-search' results.user_identifier as search_url %}
<a href="{{search_url}}">Search</a> for an issue inside of a given project.</li> <a href="{{search_url}}">Search for an issue</a> inside of a given project.</li>
<li>{% url 'create-issue' results.user_identifier as create_url %} <li>{% url 'create-issue' results.user_identifier as create_url %}
<a href="{{create_url}}">Create</a> an issue inside of a given project.</li> <a href="{{create_url}}">Create an issue</a> inside of a given project.</li>
<li>You can also create a note on an issue by searching for the issue first or
selecting it from the project page using one of the above methods.
</li>
</ul> </ul>
</p> </p>
</div> </div>
...@@ -83,4 +87,40 @@ ...@@ -83,4 +87,40 @@
{% endif %} {% endif %}
</div> </div>
</div> </div>
<!-- List the notes the user has created. -->
<div class="row">
<div class="col-12">
<h4 class="mt-4">You have created the following notes:</h4>
<!-- If there are notes, list them -->
{% url 'issue-search' results.user_identifier as search_url %}
{% url 'project-list' results.user_identifier as project_list_url%}
<p class="small short-height">(To create a new note, <a href="{{search_url}}">
search for the issue</a> you want to comment on, or look at the
<a href="{{project_list_url}}">project's detail view</a> to find an issue.
Notes marked with *** have not been approved by moderators
and therefore have not been posted to GitLab.)</p>
{% if results.linked_notes %}
<ul>
{% for note in results.linked_notes %}
<!-- if note has an gitlab_id, generate a link to the issue-detail view-->
{% if note.gitlab_id %}
{% url 'issue-detail-view' results.user_identifier note.linked_project.slug note.issue_iid as issue_url %}
<li>Posted on {{note.linked_project.name_with_namespace}} > <a href="{{issue_url}}">{{note.gitlab_issue_title}}</a>:
"{{note.body|truncatewords:20}}"</li>
<!-- else (e.g., if note is not approved), list the note and generate a link to the pending-note-detail-view -->
{% else %}
{% url 'pending-note' results.user_identifier note.linked_project.slug note.issue_iid note.pk as note_url %}
<li>(Pending) on {{note.linked_project.name_with_namespace}} > {{note.gitlab_issue_title}}:
<a href="{{note_url}}" class="mr-1">"{{note.body|capfirst|truncatewords:20}}"</a> *** </li>
{% endif %}
{% endfor %}
</ul>
</p>
{% else %}
<!-- Or tell the user are there are no issues -->
<p class="small">You have not created any Notes.</p>
{% endif %}
</div>
</div>
{% endblock %} {% endblock %}
\ No newline at end of file
...@@ -11,6 +11,8 @@ from anonticket.forms import ( ...@@ -11,6 +11,8 @@ from anonticket.forms import (
Anonymous_Ticket_Base_Search_Form, Anonymous_Ticket_Base_Search_Form,
Anonymous_Ticket_Project_Search_Form, Anonymous_Ticket_Project_Search_Form,
CreateIssueForm) CreateIssueForm)
import pprint
pp = pprint.PrettyPrinter(indent=4)
# Create your tests here. # Create your tests here.
...@@ -655,8 +657,11 @@ class TestModeratorViews(TestCase): ...@@ -655,8 +657,11 @@ class TestModeratorViews(TestCase):
# will allow test group permissions to exactly match those from file. # will allow test group permissions to exactly match those from file.
from anonticket.management.commands.create_groups import GROUPS from anonticket.management.commands.create_groups import GROUPS
from anonticket.management.commands.create_groups import Command as create_groups from anonticket.management.commands.create_groups import Command as create_groups
# Add a bad permission call so that you can test this part of the code.
GROUPS["Account Approvers"]["user identifier"].append("smash")
create_groups.handle(create_groups) create_groups.handle(create_groups)
self.Moderators = Group.objects.get(name="Moderators") self.Moderators = Group.objects.get(name="Moderators")
self.AccountApprovers = Group.objects.get(name="Account Approvers")
# Set up users to test the staff decorator and is_mod decorator. # Set up users to test the staff decorator and is_mod decorator.
UserStaffNoGroup = User.objects.create( UserStaffNoGroup = User.objects.create(
...@@ -682,6 +687,14 @@ class TestModeratorViews(TestCase): ...@@ -682,6 +687,14 @@ class TestModeratorViews(TestCase):
self.UserGroupAndStaff = UserGroupAndStaff self.UserGroupAndStaff = UserGroupAndStaff
self.Moderators.user_set.add(UserGroupAndStaff) self.Moderators.user_set.add(UserGroupAndStaff)
UserAccountApprover = User.objects.create(
username='UserAccountApprover',
password='IAmATestPassword',
is_staff=True,
)
self.UserAccountApprover = UserAccountApprover
self.AccountApprovers.user_set.add(UserAccountApprover)
# set the login redirect url for views testing # set the login redirect url for views testing
from anonticket.views import login_redirect_url as login_redirect_url from anonticket.views import login_redirect_url as login_redirect_url
self.login_redirect_url = login_redirect_url self.login_redirect_url = login_redirect_url
...@@ -711,7 +724,15 @@ class TestModeratorViews(TestCase): ...@@ -711,7 +724,15 @@ class TestModeratorViews(TestCase):
self.assertEqual(self.UserGroupAndStaff.username, "UserGroupAndStaff") self.assertEqual(self.UserGroupAndStaff.username, "UserGroupAndStaff")
self.assertTrue(self.UserGroupAndStaff.is_staff) self.assertTrue(self.UserGroupAndStaff.is_staff)
self.assertTrue(self.UserGroupAndStaff.groups.filter(name="Moderators").exists()) self.assertTrue(self.UserGroupAndStaff.groups.filter(name="Moderators").exists())
def test_user_account_approver_created_successfully(self):
"""Tese that the username exists, that the user is staff, and that
the user is part of the Account Approvers group."""
self.assertEqual(self.UserAccountApprover.username, "UserAccountApprover")
self.assertTrue(self.UserAccountApprover.is_staff)
self.assertTrue(
self.UserAccountApprover.groups.filter(name="Account Approvers").exists())
def test_moderator_view_GET_not_logged_in(self): def test_moderator_view_GET_not_logged_in(self):
"""Test that the moderator view redirects to a login form """Test that the moderator view redirects to a login form
if there is no logged in user.""" if there is no logged in user."""
...@@ -741,7 +762,22 @@ class TestModeratorViews(TestCase): ...@@ -741,7 +762,22 @@ class TestModeratorViews(TestCase):
url = reverse('moderator') url = reverse('moderator')
response = self.client.get(url) response = self.client.get(url)
self.assertTemplateNotUsed(response, 'anonticket/moderator.html') self.assertTemplateNotUsed(response, 'anonticket/moderator.html')
# print(response)
def test_moderator_view_GET_account_approver(self):
"""Test that the moderator view loads if a user is an account
approver."""
current_user = self.UserAccountApprover
self.client.force_login(current_user)
url = reverse('moderator')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
# Assert that the correct template loaded
self.assertTemplateUsed(response, 'anonticket/moderator.html')
# But that users in Account Approvers Group and not in Moderators
# Group do not have access to pending issues.
self.assertInHTML(
"You do not have permission to view pending issues at this time.",
'<p>You do not have permission to view pending issues at this time.</p>')
def test_moderator_view_GET_valid_moderator(self): def test_moderator_view_GET_valid_moderator(self):
"""Test that the moderator view displays correctly if """Test that the moderator view displays correctly if
......
...@@ -22,6 +22,7 @@ urlpatterns = [ ...@@ -22,6 +22,7 @@ urlpatterns = [
path('user/login/', views.login_view, name='login'), path('user/login/', views.login_view, name='login'),
path('user/<str:user_identifier>/login_error/', views.UserLoginErrorView.as_view(), name='user-login-error'), path('user/<str:user_identifier>/login_error/', views.UserLoginErrorView.as_view(), name='user-login-error'),
path('user/<str:user_identifier>/projects/<slug:project>/issues/<int:issue_iid>/notes/create/', views.NoteCreateView.as_view(), name='create-note'), path('user/<str:user_identifier>/projects/<slug:project>/issues/<int:issue_iid>/notes/create/', views.NoteCreateView.as_view(), name='create-note'),
path('user/<str:user_identifier>/projects/<slug:project>/issues/<int:issue_iid>/notes/<int:pk>/', views.PendingNoteDetailView.as_view(), name='pending-note'),
path('user/<str:user_identifier>/projects/<slug:project_slug>/issues/<int:gitlab_iid>/details/', views.issue_detail_view, name='issue-detail-view'), path('user/<str:user_identifier>/projects/<slug:project_slug>/issues/<int:gitlab_iid>/details/', views.issue_detail_view, name='issue-detail-view'),
path('user/<str:user_identifier>/projects/<slug:project_slug>/issues/pending/<int:pk>/', views.PendingIssueDetailView.as_view(), name='pending-issue-detail-view'), path('user/<str:user_identifier>/projects/<slug:project_slug>/issues/pending/<int:pk>/', views.PendingIssueDetailView.as_view(), name='pending-issue-detail-view'),
path('user/<str:user_identifier>/projects/all/issues/search/', views.issue_search_view, name="issue-search"), path('user/<str:user_identifier>/projects/all/issues/search/', views.issue_search_view, name="issue-search"),
......
...@@ -58,6 +58,11 @@ def get_linked_issues(UserIdentifier): ...@@ -58,6 +58,11 @@ def get_linked_issues(UserIdentifier):
linked_issues = Issue.objects.filter(linked_user=UserIdentifier) linked_issues = Issue.objects.filter(linked_user=UserIdentifier)
return linked_issues return linked_issues
def get_linked_notes(UserIdentifier):
"""Gets a list of the notes assigned to a User Identifier."""
linked_notes = Note.objects.filter(linked_user=UserIdentifier)
return linked_notes
# --------------------DECORATORS AND MIXINS----------------------------- # --------------------DECORATORS AND MIXINS-----------------------------
# Django decorators wrap functions (such as views) in other functions. # Django decorators wrap functions (such as views) in other functions.
# Mixins perform a similar function for class based views. # Mixins perform a similar function for class based views.
...@@ -209,8 +214,10 @@ def user_landing_view(request, user_identifier): ...@@ -209,8 +214,10 @@ def user_landing_view(request, user_identifier):
# results dictionary. # results dictionary.
working_user = get_user_as_object(user_identifier) working_user = get_user_as_object(user_identifier)
linked_issues = get_linked_issues(working_user) linked_issues = get_linked_issues(working_user)
results['linked_issues'] = linked_issues linked_notes = get_linked_notes(working_user)
# if found or not found, pass 'user_identifier' to context dictionary results['linked_issues'] = linked_issues
results['linked_notes'] = linked_notes
# whether user found or not found, pass 'user_identifier' to context dictionary
results['user_identifier'] = user_identifier results['user_identifier'] = user_identifier
return render(request, 'anonticket/user_landing.html', {'results': results}) return render(request, 'anonticket/user_landing.html', {'results': results})
...@@ -374,6 +381,11 @@ class NoteCreateView(PassUserIdentifierMixin, CreateView): ...@@ -374,6 +381,11 @@ class NoteCreateView(PassUserIdentifierMixin, CreateView):
working_url = reverse('issue-created', args=[user_identifier_to_pass]) working_url = reverse('issue-created', args=[user_identifier_to_pass])
return working_url return working_url
class PendingNoteDetailView(PassUserIdentifierMixin, DetailView):
"""View For Pending Notes that have not been mod approved."""
model = Note
template_name = 'anonticket/note_pending.html'
# ----------------------MODERATOR_VIEWS--------------------------------- # ----------------------MODERATOR_VIEWS---------------------------------
# Views related to moderators. Should all have decorator # Views related to moderators. Should all have decorator
# @staff_member_required, which forces staff status and allows access # @staff_member_required, which forces staff status and allows access
......
No preview for this file type
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment