improve test
This commit is contained in:
@@ -19,7 +19,7 @@ from physics.views import GenericExamViews as PhysicsExamViews
|
||||
from physics.models import Category, UserAnswer, Exam, Question
|
||||
|
||||
from generic.models import CidUser, CidUserGroup, UserUserGroup
|
||||
from rad.test_helpers import AssertNotFound, create_cid_user_and_groups
|
||||
from rad.test_helpers import AssertNotFound, ExamTester, create_cid_user_and_groups, create_exam
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
@@ -27,27 +27,6 @@ from django.contrib.auth.models import User
|
||||
APP_NAME = "physics"
|
||||
|
||||
|
||||
def create_exam(db):
|
||||
exam: Exam = PhysicsExamViews.Exam.objects.create(
|
||||
name="Cid Exam", exam_mode=True, active=True
|
||||
)
|
||||
|
||||
group1: CidUserGroup = CidUserGroup.objects.get(name="Group1")
|
||||
usergroup1: UserUserGroup = UserUserGroup.objects.get(name="Group1")
|
||||
|
||||
exam.cid_user_groups.add(group1)
|
||||
|
||||
exam.valid_cid_users.add(*group1.ciduser_set.all())
|
||||
|
||||
# exam.valid_user_users.add(user1)
|
||||
exam.valid_user_users.add(*usergroup1.users.all())
|
||||
|
||||
assert exam in group1.physics_cid_user_groups.all()
|
||||
|
||||
assert exam
|
||||
|
||||
return exam
|
||||
|
||||
|
||||
def create_category(db):
|
||||
category = Category.objects.create(category="test category")
|
||||
@@ -96,193 +75,55 @@ def create_question(exam, answer=None):
|
||||
|
||||
|
||||
def test_exams(db, client):
|
||||
create_cid_user_and_groups(db)
|
||||
exam = create_exam(db)
|
||||
|
||||
create_category(db)
|
||||
|
||||
generated_questions = [
|
||||
create_question(exam),
|
||||
create_question(exam),
|
||||
create_question(exam),
|
||||
]
|
||||
exam_tester = ExamTester(db, client, APP_NAME, PhysicsExamViews)
|
||||
|
||||
active_exams = client.get(reverse(f"{APP_NAME}:active_exams"))
|
||||
assert active_exams.status_code == 200
|
||||
assert len(json.loads(active_exams.content)["exams"]) == 0
|
||||
|
||||
cid_user_1001 = CidUser.objects.get(cid=1001)
|
||||
cid_user_1000 = CidUser.objects.get(cid=1000)
|
||||
cid_user_2001 = CidUser.objects.get(cid=2001)
|
||||
|
||||
user1 = User.objects.get(username="user1")
|
||||
user2 = User.objects.get(username="user2")
|
||||
exam_tester.create_questions(create_question)
|
||||
|
||||
users_tested = 1
|
||||
|
||||
# for cid_user in [cid_user_1001, cid_user_1000, user1, user2]:
|
||||
for cid_user in [cid_user_1001, cid_user_1000, user1, user2]:
|
||||
if isinstance(cid_user, CidUser):
|
||||
testing_cid_user = True
|
||||
else:
|
||||
testing_cid_user = False
|
||||
current_user = cid_user
|
||||
client.force_login(cid_user)
|
||||
for cid_user in exam_tester.all_users:
|
||||
|
||||
exam_tester.load_user(cid_user)
|
||||
exam = exam_tester.exam
|
||||
exam.active = False
|
||||
exam.save()
|
||||
|
||||
exam_tester.check_active_exams(0)
|
||||
|
||||
|
||||
exam.active = True
|
||||
exam.publish_results = False
|
||||
exam.save()
|
||||
start_page_res = client.get(
|
||||
reverse(f"physics:exam_start", kwargs={"pk": exam.pk})
|
||||
)
|
||||
|
||||
print(cid_user)
|
||||
exam_tester.check_onsite_start_page()
|
||||
|
||||
# Check user access to the start page
|
||||
assert start_page_res.status_code == HTTPStatus.OK
|
||||
start_page_soup = BeautifulSoup(start_page_res.content, "html.parser")
|
||||
print(start_page_soup)
|
||||
if testing_cid_user or cid_user in [user2]:
|
||||
assert "Enter your CID and passcode" in str(start_page_soup)
|
||||
else:
|
||||
assert "Start Exam" in str(start_page_soup)
|
||||
#check_onsite_start_page
|
||||
|
||||
for n, question in enumerate(generated_questions):
|
||||
if testing_cid_user:
|
||||
take_url = reverse(
|
||||
f"physics:exam_take",
|
||||
kwargs={
|
||||
"pk": exam.pk,
|
||||
"sk": n,
|
||||
"cid": cid_user.cid,
|
||||
"passcode": cid_user.passcode,
|
||||
},
|
||||
)
|
||||
else:
|
||||
take_url = reverse(
|
||||
f"physics:exam_take_user",
|
||||
kwargs={
|
||||
"pk": exam.pk,
|
||||
"sk": n,
|
||||
},
|
||||
)
|
||||
exam_tester.check_take_page()
|
||||
|
||||
cid_take_res = client.get(take_url)
|
||||
exam_tester.check_take_overview(answered=0, total=3)
|
||||
|
||||
# Check that users that have not been added cannot access the take pages
|
||||
if cid_user in [user2]:
|
||||
assert cid_take_res.status_code == HTTPStatus.NOT_FOUND
|
||||
continue
|
||||
else:
|
||||
assert cid_take_res.status_code == HTTPStatus.OK
|
||||
|
||||
cid_take_soup = BeautifulSoup(cid_take_res.content, "html.parser")
|
||||
|
||||
# Check correct user is displayed
|
||||
if testing_cid_user:
|
||||
display_user = str(cid_user.cid)
|
||||
else:
|
||||
display_user = current_user.username
|
||||
assert display_user in cid_take_soup.find("span", {"id": "user-id"}).string
|
||||
|
||||
# Check review mode alert not visible
|
||||
assert cid_take_soup.find("div", {"class": "review-mode-alert"}) is None
|
||||
|
||||
# Check that relevant buttons are being displayed
|
||||
assert cid_take_soup.find("button", {"name": "finish"}) is not None
|
||||
|
||||
if n < len(generated_questions) - 1:
|
||||
assert cid_take_soup.find("button", {"name": "next"}) is not None
|
||||
|
||||
if n > 0:
|
||||
assert cid_take_soup.find("button", {"name": "previous"}) is not None
|
||||
|
||||
assert cid_take_soup.find("span", {"id": "question-number"}).string == str(
|
||||
n + 1
|
||||
) # + 1 for 0 offset
|
||||
assert cid_take_soup.find("span", {"id": "exam-length"}).string == str(
|
||||
len(generated_questions)
|
||||
)
|
||||
|
||||
# Check that stem text is displayed
|
||||
assert (
|
||||
cid_take_soup.find("span", {"id": "question-stem"}).string
|
||||
== question.stem
|
||||
)
|
||||
|
||||
assert cid_take_soup.find("label", {"for": "id_a"}).string == question.a
|
||||
assert cid_take_soup.find("label", {"for": "id_b"}).string == question.b
|
||||
assert cid_take_soup.find("label", {"for": "id_c"}).string == question.c
|
||||
assert cid_take_soup.find("label", {"for": "id_d"}).string == question.d
|
||||
assert cid_take_soup.find("label", {"for": "id_e"}).string == question.e
|
||||
|
||||
# As we haven't submitted any answers they should all be blank
|
||||
assert (
|
||||
cid_take_soup.find("input", {"type": "checkbox", "checked": True})
|
||||
is None
|
||||
)
|
||||
|
||||
if testing_cid_user:
|
||||
take_overview_url = reverse(
|
||||
f"physics:exam_take_overview",
|
||||
kwargs={
|
||||
"pk": exam.pk,
|
||||
"cid": cid_user.cid,
|
||||
"passcode": cid_user.passcode,
|
||||
},
|
||||
)
|
||||
else:
|
||||
take_overview_url = reverse(
|
||||
f"physics:exam_take_overview_user",
|
||||
kwargs={
|
||||
"pk": exam.pk,
|
||||
},
|
||||
)
|
||||
|
||||
cid_take_overview_res = client.get(take_overview_url)
|
||||
|
||||
# Check that users that have not been added cannot access the take pages
|
||||
if cid_user in [user2]:
|
||||
assert cid_take_overview_res.status_code == HTTPStatus.NOT_FOUND
|
||||
else:
|
||||
assert cid_take_overview_res.status_code == HTTPStatus.OK
|
||||
|
||||
cid_take_overview_soup = BeautifulSoup(
|
||||
cid_take_overview_res.content, "html.parser"
|
||||
)
|
||||
assert (
|
||||
"0 out of 3 questions answered"
|
||||
in cid_take_overview_soup.find(
|
||||
"div", attrs={"class": "overview-text"}
|
||||
).string
|
||||
)
|
||||
|
||||
assert (
|
||||
len(
|
||||
cid_take_overview_soup.find_all(
|
||||
"button", attrs={"class": "unanswered"}
|
||||
)
|
||||
)
|
||||
== 3
|
||||
)
|
||||
|
||||
# Loop through questions again to test submissions
|
||||
# All physics questions have 5 parts (and require a csrf token)
|
||||
questions_len = len(generated_questions) - 1
|
||||
for n, question in enumerate(generated_questions):
|
||||
if testing_cid_user:
|
||||
questions_len = len(exam_tester.generated_questions) - 1
|
||||
for n, question in enumerate(exam_tester.generated_questions):
|
||||
if exam_tester.testing_cid_user:
|
||||
question_url = reverse(
|
||||
f"physics:exam_take",
|
||||
f"{APP_NAME}:exam_take",
|
||||
kwargs={
|
||||
"pk": exam.pk,
|
||||
"sk": n,
|
||||
"cid": cid_user.cid,
|
||||
"passcode": cid_user.passcode,
|
||||
"cid": exam_tester.current_user.cid,
|
||||
"passcode": exam_tester.current_user.passcode,
|
||||
},
|
||||
)
|
||||
else:
|
||||
question_url = reverse(
|
||||
f"physics:exam_take_user",
|
||||
f"{APP_NAME}:exam_take_user",
|
||||
kwargs={
|
||||
"pk": exam.pk,
|
||||
"sk": n,
|
||||
@@ -291,7 +132,7 @@ def test_exams(db, client):
|
||||
|
||||
cid_take_res = client.get(question_url)
|
||||
|
||||
if cid_user in [user2]:
|
||||
if cid_user in exam_tester.invalid_users:
|
||||
assert cid_take_res.status_code == HTTPStatus.NOT_FOUND
|
||||
continue
|
||||
else:
|
||||
@@ -341,255 +182,29 @@ def test_exams(db, client):
|
||||
|
||||
assert len(cid_user_answers) == users_tested
|
||||
|
||||
if testing_cid_user:
|
||||
cid_user_answer = cid_user_answers.filter(cid=cid_user.cid)[0]
|
||||
if exam_tester.testing_cid_user:
|
||||
cid_user_answer = cid_user_answers.filter(cid=exam_tester.current_user.cid)[0]
|
||||
else:
|
||||
cid_user_answer = cid_user_answers.filter(user=current_user)[0]
|
||||
cid_user_answer = cid_user_answers.filter(user=exam_tester.current_user)[0]
|
||||
|
||||
assert all(cid_user_answer.get_answers())
|
||||
|
||||
# Recheck the overview page (now that we have submitted answers)
|
||||
if testing_cid_user:
|
||||
take_overview_url = reverse(
|
||||
f"physics:exam_take_overview",
|
||||
kwargs={
|
||||
"pk": exam.pk,
|
||||
"cid": cid_user.cid,
|
||||
"passcode": cid_user.passcode,
|
||||
},
|
||||
)
|
||||
else:
|
||||
take_overview_url = reverse(
|
||||
f"physics:exam_take_overview_user",
|
||||
kwargs={
|
||||
"pk": exam.pk,
|
||||
},
|
||||
)
|
||||
exam_tester.check_take_overview(3, 3)
|
||||
|
||||
cid_take_overview_res = client.get(take_overview_url)
|
||||
|
||||
if cid_user in [user2]:
|
||||
assert cid_take_overview_res.status_code == HTTPStatus.NOT_FOUND
|
||||
else:
|
||||
assert cid_take_overview_res.status_code == HTTPStatus.OK
|
||||
exam_tester.check_cid_scores_page()
|
||||
|
||||
cid_take_overview_soup = BeautifulSoup(
|
||||
cid_take_overview_res.content, "html.parser"
|
||||
)
|
||||
assert (
|
||||
"3 out of 3 questions answered"
|
||||
in cid_take_overview_soup.find(
|
||||
"div", attrs={"class": "overview-text"}
|
||||
).string
|
||||
)
|
||||
|
||||
assert (
|
||||
len(
|
||||
cid_take_overview_soup.find_all(
|
||||
"button", attrs={"class": "unanswered"}
|
||||
)
|
||||
)
|
||||
== 0
|
||||
)
|
||||
exam_tester.check_cid_scores_user_page()
|
||||
|
||||
if testing_cid_user:
|
||||
# Get overview scores page (invalid passcode)
|
||||
AssertNotFound(
|
||||
client,
|
||||
reverse(
|
||||
f"cid_scores", kwargs={"cid": cid_user.cid, "passcode": "AAAA"}
|
||||
),
|
||||
)
|
||||
|
||||
cid_scores_url = reverse(
|
||||
f"cid_scores",
|
||||
kwargs={"cid": cid_user.cid, "passcode": cid_user.passcode},
|
||||
)
|
||||
else:
|
||||
cid_scores_url = reverse(f"user_scores")
|
||||
|
||||
# Get overview scores page
|
||||
cid_scores_res = client.get(cid_scores_url)
|
||||
|
||||
assert cid_scores_res.status_code == HTTPStatus.OK
|
||||
|
||||
cid_scores_soup = BeautifulSoup(cid_scores_res.content, "html.parser")
|
||||
|
||||
if cid_user in [user2]:
|
||||
# page should load but we shouldn't be able to find the exam
|
||||
with pytest.raises(AttributeError):
|
||||
search_exam = (
|
||||
cid_scores_soup.find("div", {"id": "exam-assigned"})
|
||||
.find("ul", {"class": exam.app_name.lower()})
|
||||
.find_all("li", attrs={"data-exam-id": exam.pk})
|
||||
)
|
||||
else:
|
||||
search_exam = (
|
||||
cid_scores_soup.find("div", {"id": "exam-assigned"})
|
||||
.find("ul", {"class": exam.app_name.lower()})
|
||||
.find_all("li", attrs={"data-exam-id": exam.pk})
|
||||
)
|
||||
assert search_exam
|
||||
assert exam.name in str(search_exam)
|
||||
assert "Active" in str(search_exam)
|
||||
# assert "Active" in assigned_exams # check it is active
|
||||
|
||||
invalid_exam = cid_scores_soup.find_all(
|
||||
"li", attrs={"data-exam-id": exam.pk + 5}
|
||||
)
|
||||
assert not invalid_exam # Check we can't find an invalid exam
|
||||
|
||||
# Check that we have a results link
|
||||
results_exam = (
|
||||
cid_scores_soup.find("div", {"id": "exam-results"})
|
||||
.find("ul", {"class": exam.app_name})
|
||||
.find_all("li", attrs={"data-exam-id": exam.pk})
|
||||
)
|
||||
assert results_exam
|
||||
assert exam.name in str(results_exam)
|
||||
assert exam.name + " test" not in str(results_exam)
|
||||
assert "Results Published" not in str(
|
||||
results_exam
|
||||
) # It should not be published yet
|
||||
|
||||
if testing_cid_user:
|
||||
AssertNotFound(
|
||||
client,
|
||||
reverse(
|
||||
f"{exam.app_name}:exam_scores_cid_user",
|
||||
kwargs={"pk": exam.pk, "cid": cid_user.cid, "passcode": "AAAA"},
|
||||
),
|
||||
)
|
||||
AssertNotFound(
|
||||
client,
|
||||
reverse(
|
||||
f"{exam.app_name}:exam_scores_cid_user",
|
||||
kwargs={
|
||||
"pk": exam.pk,
|
||||
"cid": cid_user_2001.cid,
|
||||
"passcode": cid_user_2001.passcode,
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
exam_scores_url = reverse(
|
||||
f"{exam.app_name}:exam_scores_cid_user",
|
||||
kwargs={
|
||||
"pk": exam.pk,
|
||||
"cid": cid_user.cid,
|
||||
"passcode": cid_user.passcode,
|
||||
},
|
||||
)
|
||||
else:
|
||||
exam_scores_url = reverse(
|
||||
f"{exam.app_name}:exam_scores_user",
|
||||
kwargs={"pk": exam.pk},
|
||||
)
|
||||
|
||||
# Load the exam results page
|
||||
cid_exam_scores_res = client.get(exam_scores_url)
|
||||
|
||||
if cid_user in [user2]:
|
||||
assert cid_exam_scores_res.status_code == HTTPStatus.NOT_FOUND
|
||||
# The rest of the tests are for valid users
|
||||
if exam_tester.current_user in exam_tester.invalid_users:
|
||||
continue
|
||||
else:
|
||||
assert cid_exam_scores_res.status_code == HTTPStatus.OK
|
||||
|
||||
cid_exam_scores_soup = BeautifulSoup(cid_exam_scores_res.content, "html.parser")
|
||||
|
||||
# Check that we have an alert that exams are not published
|
||||
alert = cid_exam_scores_soup.find("div", {"class": "alert"})
|
||||
assert "Results are not currently published." in str(alert)
|
||||
|
||||
# Check that we are showing enough questions / answers
|
||||
answer_lis = cid_exam_scores_soup.find_all("li", {"class": "question-part"})
|
||||
assert len(answer_lis) == 5 * len(generated_questions)
|
||||
|
||||
submitted_answers = cid_exam_scores_soup.find_all(
|
||||
"span", {"class": "submitted-user-answer"}
|
||||
)
|
||||
assert len(submitted_answers) == 5 * len(generated_questions)
|
||||
|
||||
for ans in submitted_answers:
|
||||
assert ans.text.strip() == "True"
|
||||
|
||||
# Check no correct answers are shown
|
||||
assert not cid_exam_scores_soup.find_all("span", {"class": "correct-answer"})
|
||||
|
||||
# Add another question to the exam
|
||||
extra_question = create_question(exam)
|
||||
generated_questions.append(extra_question)
|
||||
|
||||
# Load the exam results page (again)
|
||||
cid_exam_scores_res = client.get(exam_scores_url)
|
||||
cid_exam_scores_soup = BeautifulSoup(cid_exam_scores_res.content, "html.parser")
|
||||
|
||||
answer_lis = cid_exam_scores_soup.find_all("li", {"class": "question-part"})
|
||||
assert len(answer_lis) == 5 * len(generated_questions)
|
||||
|
||||
submitted_answers = cid_exam_scores_soup.find_all(
|
||||
"span", {"class": "submitted-user-answer"}
|
||||
)
|
||||
assert len(submitted_answers) == 5 * len(generated_questions)
|
||||
|
||||
for ans in submitted_answers[:-5]:
|
||||
assert ans.text.strip() == "True"
|
||||
|
||||
# The new question will not have any answers
|
||||
for ans in submitted_answers[-5:]:
|
||||
assert ans.text.strip() == ""
|
||||
|
||||
# Publish the results!
|
||||
exam.publish_results = True
|
||||
exam.save()
|
||||
|
||||
# Load the exam results page (again)
|
||||
cid_exam_scores_res = client.get(exam_scores_url)
|
||||
cid_exam_scores_soup = BeautifulSoup(cid_exam_scores_res.content, "html.parser")
|
||||
|
||||
assert (
|
||||
cid_exam_scores_soup.find("div", {"class": "alert"}) is None
|
||||
) # Check our warning banner is gone
|
||||
|
||||
# Check that we are displaying the correct answers (correctly)
|
||||
correct_answers = cid_exam_scores_soup.find_all(
|
||||
"span", {"class": "correct-answer"}
|
||||
)
|
||||
assert len(correct_answers) == 5 * len(generated_questions)
|
||||
|
||||
n = 0
|
||||
for question in generated_questions:
|
||||
for ans in question.get_answers():
|
||||
assert correct_answers[n].text.strip() == f"Correct answer: {ans}"
|
||||
n = n + 1
|
||||
|
||||
submitted_answers = cid_exam_scores_soup.find_all(
|
||||
"span", {"class": "submitted-user-answer"}
|
||||
)
|
||||
assert len(submitted_answers) == 5 * len(generated_questions)
|
||||
|
||||
for ans in submitted_answers[:-5]:
|
||||
assert ans.text.strip() == "True"
|
||||
|
||||
# The new question will not have any answers
|
||||
for ans in submitted_answers[-5:]:
|
||||
assert ans.text.strip() == ""
|
||||
|
||||
assert (
|
||||
cid_exam_scores_soup.find("div", {"class": "score-overview"})
|
||||
.find("span", {"id": "total-score"})
|
||||
.string
|
||||
== "9"
|
||||
)
|
||||
assert (
|
||||
cid_exam_scores_soup.find("div", {"class": "score-overview"})
|
||||
.find("span", {"id": "max-score"})
|
||||
.string
|
||||
== "20"
|
||||
)
|
||||
|
||||
# answer the final question
|
||||
if testing_cid_user:
|
||||
extra_question = exam_tester.generated_questions[-1]
|
||||
if exam_tester.testing_cid_user:
|
||||
cid_user_answer = UserAnswer(
|
||||
question=extra_question,
|
||||
a=True,
|
||||
@@ -608,54 +223,12 @@ def test_exams(db, client):
|
||||
c=True,
|
||||
d=False,
|
||||
e=True,
|
||||
user=current_user,
|
||||
user=exam_tester.current_user,
|
||||
exam=exam,
|
||||
)
|
||||
cid_user_answer.save()
|
||||
|
||||
cid_exam_scores_res = client.get(exam_scores_url)
|
||||
cid_exam_scores_soup = BeautifulSoup(cid_exam_scores_res.content, "html.parser")
|
||||
exam_tester.check_cid_scores_user_page2()
|
||||
|
||||
# As we have answered correctly the new score should be increased by 5
|
||||
assert (
|
||||
cid_exam_scores_soup.find("div", {"class": "score-overview"})
|
||||
.find("span", {"id": "total-score"})
|
||||
.string
|
||||
== "14"
|
||||
)
|
||||
|
||||
# Check what happens when exam is not active
|
||||
exam.active = False
|
||||
exam.save()
|
||||
|
||||
start_page_res = client.get(
|
||||
reverse(f"physics:exam_start", kwargs={"pk": exam.pk})
|
||||
)
|
||||
start_page_soup = BeautifulSoup(start_page_res.content, "html.parser")
|
||||
|
||||
assert "Exam is currently inactive" in start_page_soup.text
|
||||
|
||||
if testing_cid_user:
|
||||
cid_take_url = reverse(
|
||||
f"physics:exam_take",
|
||||
kwargs={
|
||||
"pk": exam.pk,
|
||||
"sk": 0,
|
||||
"cid": cid_user.cid,
|
||||
"passcode": cid_user.passcode,
|
||||
},
|
||||
)
|
||||
else:
|
||||
cid_take_url = reverse(
|
||||
f"physics:exam_take_user",
|
||||
kwargs={
|
||||
"pk": exam.pk,
|
||||
"sk": 0,
|
||||
},
|
||||
)
|
||||
cid_take_res = client.get(cid_take_url)
|
||||
cid_take_soup = BeautifulSoup(cid_take_res.content, "html.parser")
|
||||
assert "Exam is currently inactive" in cid_take_soup.text
|
||||
|
||||
generated_questions.pop(-1).delete()
|
||||
users_tested = users_tested + 1
|
||||
|
||||
+498
-5
@@ -1,12 +1,22 @@
|
||||
from http import HTTPStatus
|
||||
import json
|
||||
from typing import Callable
|
||||
|
||||
from generic.models import CidUser, CidUserGroup, UserUserGroup
|
||||
from bs4 import BeautifulSoup
|
||||
import pytest
|
||||
|
||||
from generic.models import CidUser, CidUserGroup, UserUserGroup, ExamBase
|
||||
from generic.views import ExamViews
|
||||
from django.contrib.auth.models import User
|
||||
from django.urls import reverse
|
||||
|
||||
|
||||
def AssertNotFound(client, url):
|
||||
"""Helper to quickly test for urls that should return a 404 (NOT FOUND) error"""
|
||||
response = client.get(url)
|
||||
assert response.status_code == HTTPStatus.NOT_FOUND, f"Response content: {response.content}"
|
||||
assert (
|
||||
response.status_code == HTTPStatus.NOT_FOUND
|
||||
), f"Response content: {response.content}"
|
||||
|
||||
|
||||
def create_cid_user_and_groups(db):
|
||||
@@ -21,9 +31,492 @@ def create_cid_user_and_groups(db):
|
||||
assert cid_user_1001.cid == 1001
|
||||
|
||||
# Create UserUsers
|
||||
user1 = User.objects.create_user(username="user1", email="test@email.com", password="test1234")
|
||||
user2 = User.objects.create_user(username="user2", email="test2@email.com", password="test1234")
|
||||
user1 = User.objects.create_user(
|
||||
username="user1", email="test@email.com", password="test1234"
|
||||
)
|
||||
user2 = User.objects.create_user(
|
||||
username="user2", email="test2@email.com", password="test1234"
|
||||
)
|
||||
usergroup1 = UserUserGroup.objects.create(name="Group1")
|
||||
usergroup1.users.add(user1)
|
||||
usergroup2 = UserUserGroup.objects.create(name="Group2")
|
||||
usergroup2.users.add(user2)
|
||||
usergroup2.users.add(user2)
|
||||
|
||||
|
||||
def create_exam(db, ExamViews: ExamViews):
|
||||
exam: ExamBase = ExamViews.Exam.objects.create(
|
||||
name="Cid Exam", exam_mode=True, active=True
|
||||
)
|
||||
|
||||
cidgroup1: CidUserGroup = CidUserGroup.objects.get(name="Group1")
|
||||
usergroup1: UserUserGroup = UserUserGroup.objects.get(name="Group1")
|
||||
|
||||
exam.cid_user_groups.add(cidgroup1)
|
||||
|
||||
exam.valid_cid_users.add(*cidgroup1.ciduser_set.all())
|
||||
|
||||
# exam.valid_user_users.add(user1)
|
||||
exam.valid_user_users.add(*usergroup1.users.all())
|
||||
|
||||
assert exam
|
||||
|
||||
return exam
|
||||
|
||||
|
||||
class ExamTester:
|
||||
def __init__(self, db, client, app_name, ExamViews) -> None:
|
||||
self.db = db
|
||||
self.client = client
|
||||
self.app_name = app_name
|
||||
|
||||
create_cid_user_and_groups(db)
|
||||
self.exam = exam = create_exam(db, ExamViews)
|
||||
|
||||
cidgroup1: CidUserGroup = CidUserGroup.objects.get(name="Group1")
|
||||
assert exam in cidgroup1.physics_cid_user_groups.all()
|
||||
|
||||
cid_user_1001 = CidUser.objects.get(cid=1001)
|
||||
cid_user_1000 = CidUser.objects.get(cid=1000)
|
||||
self.cid_user_2001 = cid_user_2001 = CidUser.objects.get(cid=2001)
|
||||
|
||||
user1 = User.objects.get(username="user1")
|
||||
user2 = User.objects.get(username="user2")
|
||||
|
||||
self.all_users = [cid_user_1000, cid_user_1001, cid_user_2001, user1, user2]
|
||||
|
||||
self.cid_users = [cid_user_1000, cid_user_1001, cid_user_2001]
|
||||
|
||||
self.invalid_users = [cid_user_2001, user2]
|
||||
|
||||
self.current_user = None
|
||||
self.generated_questions = []
|
||||
|
||||
def load_user(self, user):
|
||||
self.client.logout()
|
||||
|
||||
self.current_user = user
|
||||
|
||||
if isinstance(self.current_user, CidUser):
|
||||
self.testing_cid_user = True
|
||||
else:
|
||||
self.testing_cid_user = False
|
||||
|
||||
self.client.force_login(user)
|
||||
|
||||
def create_questions(self, create_question_func: Callable):
|
||||
self.create_question_func = create_question_func
|
||||
|
||||
self.generated_questions = [
|
||||
create_question_func(self.exam),
|
||||
create_question_func(self.exam),
|
||||
create_question_func(self.exam),
|
||||
]
|
||||
|
||||
def add_question(self):
|
||||
self.generated_questions.append(self.create_question_func(self.exam))
|
||||
|
||||
def check_active_exams(self, n):
|
||||
active_exams = self.client.get(reverse(f"{self.app_name}:active_exams"))
|
||||
assert active_exams.status_code == 200
|
||||
assert len(json.loads(active_exams.content)["exams"]) == n, f"Current user: {self.current_user}"
|
||||
|
||||
def check_onsite_start_page(self):
|
||||
start_page_res = self.client.get(
|
||||
reverse(f"{self.app_name}:exam_start", kwargs={"pk": self.exam.pk})
|
||||
)
|
||||
|
||||
# Check user access to the start page
|
||||
assert start_page_res.status_code == HTTPStatus.OK
|
||||
start_page_soup = BeautifulSoup(start_page_res.content, "html.parser")
|
||||
if (
|
||||
self.current_user in self.cid_users
|
||||
or self.current_user in self.invalid_users
|
||||
):
|
||||
assert "Enter your CID and passcode" in str(start_page_soup)
|
||||
else:
|
||||
assert "Start Exam" in str(start_page_soup)
|
||||
|
||||
def check_take_page(self):
|
||||
for n, question in enumerate(self.generated_questions):
|
||||
if self.testing_cid_user:
|
||||
take_url = reverse(
|
||||
f"{self.app_name}:exam_take",
|
||||
kwargs={
|
||||
"pk": self.exam.pk,
|
||||
"sk": n,
|
||||
"cid": self.current_user.cid,
|
||||
"passcode": self.current_user.passcode,
|
||||
},
|
||||
)
|
||||
else:
|
||||
take_url = reverse(
|
||||
f"{self.app_name}:exam_take_user",
|
||||
kwargs={
|
||||
"pk": self.exam.pk,
|
||||
"sk": n,
|
||||
},
|
||||
)
|
||||
|
||||
cid_take_res = self.client.get(take_url)
|
||||
|
||||
# Check that users that have not been added cannot access the take pages
|
||||
if self.current_user in self.invalid_users:
|
||||
assert cid_take_res.status_code == HTTPStatus.NOT_FOUND
|
||||
continue
|
||||
else:
|
||||
assert cid_take_res.status_code == HTTPStatus.OK, f"Current user: {self.current_user}"
|
||||
|
||||
cid_take_soup = BeautifulSoup(cid_take_res.content, "html.parser")
|
||||
|
||||
# Check correct user is displayed
|
||||
if self.testing_cid_user:
|
||||
display_user = str(self.current_user.cid)
|
||||
else:
|
||||
display_user = self.current_user.username
|
||||
assert display_user in cid_take_soup.find("span", {"id": "user-id"}).string
|
||||
|
||||
# Check review mode alert not visible
|
||||
assert cid_take_soup.find("div", {"class": "review-mode-alert"}) is None
|
||||
|
||||
# Check that relevant buttons are being displayed
|
||||
assert cid_take_soup.find("button", {"name": "finish"}) is not None
|
||||
|
||||
if n < len(self.generated_questions) - 1:
|
||||
assert cid_take_soup.find("button", {"name": "next"}) is not None
|
||||
|
||||
if n > 0:
|
||||
assert cid_take_soup.find("button", {"name": "previous"}) is not None
|
||||
|
||||
assert cid_take_soup.find("span", {"id": "question-number"}).string == str(
|
||||
n + 1
|
||||
) # + 1 for 0 offset
|
||||
assert cid_take_soup.find("span", {"id": "exam-length"}).string == str(
|
||||
len(self.generated_questions)
|
||||
)
|
||||
|
||||
# Check that stem text is displayed
|
||||
assert (
|
||||
cid_take_soup.find("span", {"id": "question-stem"}).string
|
||||
== question.stem
|
||||
)
|
||||
|
||||
assert cid_take_soup.find("label", {"for": "id_a"}).string == question.a
|
||||
assert cid_take_soup.find("label", {"for": "id_b"}).string == question.b
|
||||
assert cid_take_soup.find("label", {"for": "id_c"}).string == question.c
|
||||
assert cid_take_soup.find("label", {"for": "id_d"}).string == question.d
|
||||
assert cid_take_soup.find("label", {"for": "id_e"}).string == question.e
|
||||
|
||||
# As we haven't submitted any answers they should all be blank
|
||||
assert (
|
||||
cid_take_soup.find("input", {"type": "checkbox", "checked": True})
|
||||
is None
|
||||
)
|
||||
|
||||
def check_take_overview(self, answered, total):
|
||||
if self.testing_cid_user:
|
||||
take_overview_url = reverse(
|
||||
f"{self.app_name}:exam_take_overview",
|
||||
kwargs={
|
||||
"pk": self.exam.pk,
|
||||
"cid": self.current_user.cid,
|
||||
"passcode": self.current_user.passcode,
|
||||
},
|
||||
)
|
||||
else:
|
||||
take_overview_url = reverse(
|
||||
f"{self.app_name}:exam_take_overview_user",
|
||||
kwargs={
|
||||
"pk": self.exam.pk,
|
||||
},
|
||||
)
|
||||
|
||||
cid_take_overview_res = self.client.get(take_overview_url)
|
||||
|
||||
# Check that users that have not been added cannot access the take pages
|
||||
if self.current_user in self.invalid_users:
|
||||
assert cid_take_overview_res.status_code == HTTPStatus.NOT_FOUND
|
||||
else:
|
||||
assert cid_take_overview_res.status_code == HTTPStatus.OK
|
||||
|
||||
cid_take_overview_soup = BeautifulSoup(
|
||||
cid_take_overview_res.content, "html.parser"
|
||||
)
|
||||
print(self.generated_questions)
|
||||
assert (
|
||||
f"{answered} out of 3 questions answered"
|
||||
in cid_take_overview_soup.find(
|
||||
"div", attrs={"class": "overview-text"}
|
||||
).string
|
||||
)
|
||||
|
||||
assert (
|
||||
len(
|
||||
cid_take_overview_soup.find_all(
|
||||
"button", attrs={"class": "unanswered"}
|
||||
)
|
||||
)
|
||||
== total - answered
|
||||
)
|
||||
|
||||
def check_cid_scores_page(self):
|
||||
if self.testing_cid_user:
|
||||
# Get overview scores page (invalid passcode)
|
||||
AssertNotFound(
|
||||
self.client,
|
||||
reverse(
|
||||
f"cid_scores",
|
||||
kwargs={"cid": self.current_user.cid, "passcode": "AAAA"},
|
||||
),
|
||||
)
|
||||
|
||||
cid_scores_url = reverse(
|
||||
f"cid_scores",
|
||||
kwargs={
|
||||
"cid": self.current_user.cid,
|
||||
"passcode": self.current_user.passcode,
|
||||
},
|
||||
)
|
||||
else:
|
||||
cid_scores_url = reverse(f"user_scores")
|
||||
|
||||
# Get overview scores page
|
||||
cid_scores_res = self.client.get(cid_scores_url)
|
||||
|
||||
assert cid_scores_res.status_code == HTTPStatus.OK
|
||||
|
||||
cid_scores_soup = BeautifulSoup(cid_scores_res.content, "html.parser")
|
||||
|
||||
if self.current_user in self.invalid_users:
|
||||
# page should load but we shouldn't be able to find the exam
|
||||
with pytest.raises(AttributeError):
|
||||
search_exam = (
|
||||
cid_scores_soup.find("div", {"id": "exam-assigned"})
|
||||
.find("ul", {"class": self.exam.app_name.lower()})
|
||||
.find_all("li", attrs={"data-exam-id": self.exam.pk})
|
||||
)
|
||||
else:
|
||||
search_exam = (
|
||||
cid_scores_soup.find("div", {"id": "exam-assigned"})
|
||||
.find("ul", {"class": self.exam.app_name.lower()})
|
||||
.find_all("li", attrs={"data-exam-id": self.exam.pk})
|
||||
)
|
||||
assert search_exam
|
||||
assert self.exam.name in str(search_exam)
|
||||
assert "Active" in str(search_exam)
|
||||
# assert "Active" in assigned_exams # check it is active
|
||||
|
||||
invalid_exam = cid_scores_soup.find_all(
|
||||
"li", attrs={"data-exam-id": self.exam.pk + 5}
|
||||
)
|
||||
assert not invalid_exam # Check we can't find an invalid exam
|
||||
|
||||
# Check that we have a results link
|
||||
results_exam = (
|
||||
cid_scores_soup.find("div", {"id": "exam-results"})
|
||||
.find("ul", {"class": self.exam.app_name})
|
||||
.find_all("li", attrs={"data-exam-id": self.exam.pk})
|
||||
)
|
||||
assert results_exam
|
||||
assert self.exam.name in str(results_exam)
|
||||
assert self.exam.name + " test" not in str(results_exam)
|
||||
assert "Results Published" not in str(
|
||||
results_exam
|
||||
) # It should not be published yet
|
||||
|
||||
def check_cid_scores_user_page(self):
|
||||
if self.testing_cid_user:
|
||||
AssertNotFound(
|
||||
self.client,
|
||||
reverse(
|
||||
f"{self.exam.app_name}:exam_scores_cid_user",
|
||||
kwargs={
|
||||
"pk": self.exam.pk,
|
||||
"cid": self.current_user.cid,
|
||||
"passcode": "AAAA",
|
||||
},
|
||||
),
|
||||
)
|
||||
AssertNotFound(
|
||||
self.client,
|
||||
reverse(
|
||||
f"{self.exam.app_name}:exam_scores_cid_user",
|
||||
kwargs={
|
||||
"pk": self.exam.pk,
|
||||
"cid": self.cid_user_2001.cid,
|
||||
"passcode": self.cid_user_2001.passcode,
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
exam_scores_url = reverse(
|
||||
f"{self.exam.app_name}:exam_scores_cid_user",
|
||||
kwargs={
|
||||
"pk": self.exam.pk,
|
||||
"cid": self.current_user.cid,
|
||||
"passcode": self.current_user.passcode,
|
||||
},
|
||||
)
|
||||
else:
|
||||
exam_scores_url = reverse(
|
||||
f"{self.exam.app_name}:exam_scores_user",
|
||||
kwargs={"pk": self.exam.pk},
|
||||
)
|
||||
|
||||
self.exam_scores_url = exam_scores_url
|
||||
|
||||
# Load the exam results page
|
||||
cid_exam_scores_res = self.client.get(exam_scores_url)
|
||||
|
||||
if self.current_user in self.invalid_users:
|
||||
assert cid_exam_scores_res.status_code == HTTPStatus.NOT_FOUND
|
||||
return
|
||||
else:
|
||||
assert cid_exam_scores_res.status_code == HTTPStatus.OK
|
||||
|
||||
cid_exam_scores_soup = BeautifulSoup(cid_exam_scores_res.content, "html.parser")
|
||||
|
||||
# Check that we have an alert that exams are not published
|
||||
alert = cid_exam_scores_soup.find("div", {"class": "alert"})
|
||||
assert "Results are not currently published." in str(alert)
|
||||
|
||||
# Check that we are showing enough questions / answers
|
||||
answer_lis = cid_exam_scores_soup.find_all("li", {"class": "question-part"})
|
||||
assert len(answer_lis) == 5 * len(self.generated_questions)
|
||||
|
||||
submitted_answers = cid_exam_scores_soup.find_all(
|
||||
"span", {"class": "submitted-user-answer"}
|
||||
)
|
||||
assert len(submitted_answers) == 5 * len(self.generated_questions)
|
||||
|
||||
for ans in submitted_answers:
|
||||
assert ans.text.strip() == "True"
|
||||
|
||||
# Check no correct answers are shown
|
||||
assert not cid_exam_scores_soup.find_all("span", {"class": "correct-answer"})
|
||||
|
||||
# Add another question to the exam
|
||||
self.add_question()
|
||||
|
||||
# Load the exam results page (again)
|
||||
cid_exam_scores_res = self.client.get(exam_scores_url)
|
||||
cid_exam_scores_soup = BeautifulSoup(cid_exam_scores_res.content, "html.parser")
|
||||
|
||||
answer_lis = cid_exam_scores_soup.find_all("li", {"class": "question-part"})
|
||||
assert len(answer_lis) == 5 * len(self.generated_questions)
|
||||
|
||||
submitted_answers = cid_exam_scores_soup.find_all(
|
||||
"span", {"class": "submitted-user-answer"}
|
||||
)
|
||||
assert len(submitted_answers) == 5 * len(self.generated_questions)
|
||||
|
||||
for ans in submitted_answers[:-5]:
|
||||
assert ans.text.strip() == "True"
|
||||
|
||||
# The new question will not have any answers
|
||||
for ans in submitted_answers[-5:]:
|
||||
assert ans.text.strip() == ""
|
||||
|
||||
# Publish the results!
|
||||
self.exam.publish_results = True
|
||||
self.exam.save()
|
||||
|
||||
# Load the exam results page (again)
|
||||
cid_exam_scores_res = self.client.get(exam_scores_url)
|
||||
cid_exam_scores_soup = BeautifulSoup(cid_exam_scores_res.content, "html.parser")
|
||||
|
||||
assert (
|
||||
cid_exam_scores_soup.find("div", {"class": "alert"}) is None
|
||||
) # Check our warning banner is gone
|
||||
|
||||
# Check that we are displaying the correct answers (correctly)
|
||||
correct_answers = cid_exam_scores_soup.find_all(
|
||||
"span", {"class": "correct-answer"}
|
||||
)
|
||||
assert len(correct_answers) == 5 * len(self.generated_questions)
|
||||
|
||||
n = 0
|
||||
for question in self.generated_questions:
|
||||
for ans in question.get_answers():
|
||||
assert correct_answers[n].text.strip() == f"Correct answer: {ans}"
|
||||
n = n + 1
|
||||
|
||||
submitted_answers = cid_exam_scores_soup.find_all(
|
||||
"span", {"class": "submitted-user-answer"}
|
||||
)
|
||||
assert len(submitted_answers) == 5 * len(self.generated_questions)
|
||||
|
||||
for ans in submitted_answers[:-5]:
|
||||
assert ans.text.strip() == "True"
|
||||
|
||||
# The new question will not have any answers
|
||||
for ans in submitted_answers[-5:]:
|
||||
assert ans.text.strip() == ""
|
||||
|
||||
assert (
|
||||
cid_exam_scores_soup.find("div", {"class": "score-overview"})
|
||||
.find("span", {"id": "total-score"})
|
||||
.string
|
||||
== "9"
|
||||
)
|
||||
assert (
|
||||
cid_exam_scores_soup.find("div", {"class": "score-overview"})
|
||||
.find("span", {"id": "max-score"})
|
||||
.string
|
||||
== "20"
|
||||
)
|
||||
|
||||
|
||||
|
||||
def check_cid_scores_user_page2(self):
|
||||
|
||||
cid_exam_scores_res = self.client.get(self.exam_scores_url)
|
||||
|
||||
cid_exam_scores_soup = BeautifulSoup(cid_exam_scores_res.content, "html.parser")
|
||||
|
||||
|
||||
|
||||
# As we have answered correctly the new score should be increased by 5
|
||||
assert (
|
||||
cid_exam_scores_soup.find("div", {"class": "score-overview"})
|
||||
.find("span", {"id": "total-score"})
|
||||
.string
|
||||
== "14"
|
||||
), f"Current user: {self.current_user}"
|
||||
|
||||
# Check what happens when exam is not active
|
||||
self.exam.active = False
|
||||
self.exam.save()
|
||||
|
||||
start_page_res = self.client.get(
|
||||
reverse(f"{self.app_name}:exam_start", kwargs={"pk": self.exam.pk})
|
||||
)
|
||||
start_page_soup = BeautifulSoup(start_page_res.content, "html.parser")
|
||||
|
||||
assert "Exam is currently inactive" in start_page_soup.text
|
||||
|
||||
if self.testing_cid_user:
|
||||
cid_take_url = reverse(
|
||||
f"{self.app_name}:exam_take",
|
||||
kwargs={
|
||||
"pk": self.exam.pk,
|
||||
"sk": 0,
|
||||
"cid": self.current_user.cid,
|
||||
"passcode": self.current_user.passcode,
|
||||
},
|
||||
)
|
||||
else:
|
||||
cid_take_url = reverse(
|
||||
f"{self.app_name}:exam_take_user",
|
||||
kwargs={
|
||||
"pk": self.exam.pk,
|
||||
"sk": 0,
|
||||
},
|
||||
)
|
||||
cid_take_res = self.client.get(cid_take_url)
|
||||
cid_take_soup = BeautifulSoup(cid_take_res.content, "html.parser")
|
||||
assert "Exam is currently inactive" in cid_take_soup.text
|
||||
|
||||
# Delete the last question, from both the list
|
||||
q= self.generated_questions.pop()
|
||||
# and the database
|
||||
q.delete()
|
||||
Reference in New Issue
Block a user