247 lines
8.1 KiB
Python
247 lines
8.1 KiB
Python
from http import HTTPStatus
|
|
import json
|
|
import os
|
|
from re import A
|
|
from django.conf import settings
|
|
import pytest
|
|
|
|
import tempfile
|
|
|
|
# from django.contrib.auth.models import User
|
|
from django.urls import reverse
|
|
|
|
from rich.pretty import pprint
|
|
|
|
from bs4 import BeautifulSoup
|
|
|
|
from sbas.views import GenericExamViews as SbaExamViews
|
|
|
|
from sbas.models import Category, UserAnswer, Exam, Question
|
|
|
|
from generic.models import CidUser, CidUserGroup, UserUserGroup
|
|
from rad.tests.test_helpers import AssertNotFound, ExamTester, create_cid_user_and_groups, create_exam
|
|
|
|
from django.contrib.auth.models import User
|
|
|
|
|
|
APP_NAME = "sbas"
|
|
|
|
|
|
|
|
def create_category(db):
|
|
category = Category.objects.create(category="test category")
|
|
category.save()
|
|
category = Category.objects.create(category="test category 2")
|
|
category.save()
|
|
|
|
|
|
def create_question(exam, answer=None):
|
|
# Just assign random stuff
|
|
|
|
if answer is None:
|
|
best_answer = "a"
|
|
else:
|
|
best_answer = answer
|
|
question: Question = Question.objects.create(
|
|
stem="test question stem",
|
|
a_answer="question a text",
|
|
a_feedback="question a feedback",
|
|
b_answer="question b text",
|
|
b_feedback="question b feedback",
|
|
c_answer="question c text",
|
|
c_feedback="question c feedback",
|
|
d_answer="question d text",
|
|
d_feedback="question d feedback",
|
|
e_answer="question e text",
|
|
e_feedback="question e feedback",
|
|
best_answer=best_answer,
|
|
)
|
|
question.exams.set([exam])
|
|
|
|
return question
|
|
|
|
|
|
def test_exams(db, client):
|
|
create_category(db)
|
|
|
|
exam_tester = ExamTester(db, client, APP_NAME, SbaExamViews)
|
|
|
|
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 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()
|
|
|
|
exam_tester.check_onsite_start_page()
|
|
|
|
#check_onsite_start_page
|
|
|
|
exam_tester.check_take_page()
|
|
|
|
exam_tester.check_take_overview(answered=0, total=3)
|
|
|
|
|
|
# Loop through questions again to test submissions
|
|
# All physics questions have 5 parts (and require a csrf token)
|
|
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"{APP_NAME}:exam_take",
|
|
kwargs={
|
|
"pk": exam.pk,
|
|
"sk": n,
|
|
"cid": exam_tester.current_user.cid,
|
|
"passcode": exam_tester.current_user.passcode,
|
|
},
|
|
)
|
|
else:
|
|
question_url = reverse(
|
|
f"{APP_NAME}:exam_take_user",
|
|
kwargs={
|
|
"pk": exam.pk,
|
|
"sk": n,
|
|
},
|
|
)
|
|
|
|
# Request the full page which now loads the fragment via HTMX;
|
|
# fetch the fragment endpoint directly to get the form and CSRF token.
|
|
cid_take_res = client.get(question_url)
|
|
|
|
if cid_user in exam_tester.invalid_users:
|
|
assert cid_take_res.status_code == HTTPStatus.NOT_FOUND
|
|
continue
|
|
else:
|
|
assert cid_take_res.status_code == HTTPStatus.OK
|
|
|
|
# The full page should include the HTMX container which will load
|
|
# the fragment client-side. Assert the container exists and has an
|
|
# hx-get attribute pointing to a fragment endpoint.
|
|
main_soup = BeautifulSoup(cid_take_res.content, "html.parser")
|
|
container = main_soup.find("div", attrs={"id": "question-fragment"})
|
|
assert container is not None, "Missing #question-fragment container"
|
|
hxget = container.get("hx-get") or container.get("data-hx-get")
|
|
assert hxget and "take_fragment" in hxget, f"Container hx-get not pointing to fragment: {hxget}"
|
|
|
|
# Build fragment URL and fetch it to obtain CSRF token and controls
|
|
if exam_tester.testing_cid_user:
|
|
frag_url = reverse(
|
|
f"{APP_NAME}:exam_take_fragment",
|
|
kwargs={
|
|
"pk": exam.pk,
|
|
"sk": n,
|
|
"cid": exam_tester.current_user.cid,
|
|
"passcode": exam_tester.current_user.passcode,
|
|
},
|
|
)
|
|
else:
|
|
frag_url = reverse(
|
|
f"{APP_NAME}:exam_take_fragment_user",
|
|
kwargs={
|
|
"pk": exam.pk,
|
|
"sk": n,
|
|
},
|
|
)
|
|
|
|
frag_res = client.get(frag_url)
|
|
assert frag_res.status_code == HTTPStatus.OK
|
|
cid_take_soup = BeautifulSoup(frag_res.content, "html.parser")
|
|
|
|
# Extract csrftoken from fragment
|
|
csrftoken = cid_take_soup.find("input", attrs={"name": "csrfmiddlewaretoken"})["value"]
|
|
|
|
# Ensure fragment contains the question stem and answer list
|
|
assert "test question stem" in frag_res.text
|
|
# check answer choices exist with data-ans attributes
|
|
assert cid_take_soup.find("li", attrs={"data-ans": "a"}), "Missing answer choice a in fragment"
|
|
assert "question a text" in frag_res.text
|
|
# Send answers (all TRUE)
|
|
post_json = {
|
|
"csrfmiddlewaretoken": csrftoken,
|
|
"answer": "a",
|
|
"next": "",
|
|
}
|
|
res = client.post(question_url, data=post_json)
|
|
|
|
next_button = cid_take_soup.find("button", attrs={"name": "next"})
|
|
|
|
# Our final question will trigger a bad request (it shouldn't show a next button)
|
|
# We currently do still save the form though....
|
|
if questions_len == n:
|
|
assert next_button == None
|
|
assert res.status_code == HTTPStatus.BAD_REQUEST
|
|
else:
|
|
assert next_button
|
|
assert res.status_code == HTTPStatus.FOUND
|
|
|
|
overview_button = cid_take_soup.find("button", attrs={"name": "finish"})
|
|
assert overview_button
|
|
|
|
previous_button = cid_take_soup.find("button", attrs={"name": "previous"})
|
|
|
|
if n > 0:
|
|
assert previous_button
|
|
else:
|
|
assert not previous_button
|
|
|
|
# Filter answer to the question we just submitted to
|
|
cid_user_answers = question.cid_user_answers.all()
|
|
|
|
assert len(cid_user_answers) == users_tested
|
|
|
|
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=exam_tester.current_user)[0]
|
|
|
|
assert all(cid_user_answer.get_answer())
|
|
|
|
# Recheck the overview page (now that we have submitted answers)
|
|
exam_tester.check_take_overview(3, 3)
|
|
|
|
|
|
exam_tester.check_cid_scores_page()
|
|
|
|
|
|
exam_tester.check_cid_scores_user_page()
|
|
|
|
# The rest of the tests are for valid users
|
|
if exam_tester.current_user in exam_tester.invalid_users:
|
|
continue
|
|
|
|
# answer the final question
|
|
extra_question = exam_tester.generated_questions[-1]
|
|
if exam_tester.testing_cid_user:
|
|
cid_user_answer = UserAnswer(
|
|
question=extra_question,
|
|
answer = "a",
|
|
cid=cid_user.cid,
|
|
exam=exam,
|
|
)
|
|
else:
|
|
cid_user_answer = UserAnswer(
|
|
question=extra_question,
|
|
answer = "a",
|
|
user=exam_tester.current_user,
|
|
exam=exam,
|
|
)
|
|
cid_user_answer.save()
|
|
|
|
exam_tester.check_cid_scores_user_page2()
|
|
|
|
|
|
users_tested = users_tested + 1
|