|
# retoor <retoor@molodetz.nl>
|
|
|
|
import requests
|
|
|
|
from tests.conftest import BASE_URL
|
|
|
|
JSON = {"Accept": "application/json"}
|
|
|
|
CHOICE = {
|
|
"kind": "single_choice",
|
|
"prompt": "Which journal mode allows concurrent readers?",
|
|
"points": 2,
|
|
"options": "DELETE\nWAL",
|
|
"correct_indexes": "1",
|
|
}
|
|
|
|
NUMERIC = {
|
|
"kind": "numeric",
|
|
"prompt": "How many bytes are in a kibibyte?",
|
|
"points": 1,
|
|
"numeric_value": "1024",
|
|
"numeric_tolerance": "0",
|
|
}
|
|
|
|
DIALOG_CONFIRM = ".dialog-confirm"
|
|
|
|
_counter = [0]
|
|
|
|
|
|
def _author_quiz(title, questions=(CHOICE, NUMERIC), **settings):
|
|
_counter[0] += 1
|
|
name = f"play{abs(hash(title)) % 10**8}{_counter[0]}"
|
|
session = requests.Session()
|
|
session.post(
|
|
f"{BASE_URL}/auth/signup",
|
|
data={
|
|
"username": name,
|
|
"email": f"{name}@t.dev",
|
|
"password": "secret123",
|
|
"confirm_password": "secret123",
|
|
},
|
|
allow_redirects=True,
|
|
)
|
|
payload = {"title": title, "description": "d", **settings}
|
|
slug = session.post(f"{BASE_URL}/quizzes/create", data=payload, headers=JSON).json()["data"][
|
|
"slug"
|
|
]
|
|
for question in questions:
|
|
session.post(f"{BASE_URL}/quizzes/{slug}/questions", data=question, headers=JSON)
|
|
session.post(f"{BASE_URL}/quizzes/{slug}/publish", headers=JSON)
|
|
return slug
|
|
|
|
|
|
def _start(page, slug):
|
|
page.goto(f"{BASE_URL}/quizzes/{slug}", wait_until="domcontentloaded")
|
|
page.locator(".quiz-detail-actions button:has-text('Start quiz')").click()
|
|
page.wait_for_url("**/attempts/**", wait_until="domcontentloaded")
|
|
page.locator("dp-quiz-player").wait_for(state="visible")
|
|
|
|
|
|
def test_starting_opens_the_player(alice):
|
|
page, _ = alice
|
|
slug = _author_quiz("Player opens quiz")
|
|
_start(page, slug)
|
|
assert page.locator(".quiz-question").count() == 2
|
|
page.locator(".quiz-hud").wait_for(state="visible")
|
|
|
|
|
|
def test_the_player_renders_a_real_form_per_question(alice):
|
|
page, _ = alice
|
|
slug = _author_quiz("Player forms quiz")
|
|
_start(page, slug)
|
|
forms = page.locator("form[data-quiz-answer-form]")
|
|
assert forms.count() == 2
|
|
assert forms.first.get_attribute("method").lower() == "post"
|
|
|
|
|
|
def test_answering_a_choice_shows_the_grade_in_place(alice):
|
|
page, _ = alice
|
|
slug = _author_quiz("Player grade quiz")
|
|
_start(page, slug)
|
|
question = page.locator(".quiz-question-single_choice").first
|
|
question.locator("input[type='radio']").last.check()
|
|
question.locator("button:has-text('Submit answer')").click()
|
|
question.locator(".quiz-grade-correct").wait_for(state="visible")
|
|
assert "Correct" in question.locator(".quiz-grade-verdict").inner_text()
|
|
|
|
|
|
def test_a_wrong_answer_is_reported(alice):
|
|
page, _ = alice
|
|
slug = _author_quiz("Player wrong quiz")
|
|
_start(page, slug)
|
|
question = page.locator(".quiz-question-single_choice").first
|
|
question.locator("input[type='radio']").first.check()
|
|
question.locator("button:has-text('Submit answer')").click()
|
|
question.locator(".quiz-grade-wrong").wait_for(state="visible")
|
|
|
|
|
|
def test_the_hud_updates_after_an_answer(alice):
|
|
page, _ = alice
|
|
slug = _author_quiz("Player hud quiz")
|
|
_start(page, slug)
|
|
before = page.locator("[data-quiz-progress]").inner_text()
|
|
question = page.locator(".quiz-question-single_choice").first
|
|
question.locator("input[type='radio']").last.check()
|
|
question.locator("button:has-text('Submit answer')").click()
|
|
question.locator(".quiz-grade").wait_for(state="visible")
|
|
page.wait_for_function(
|
|
"before => document.querySelector('[data-quiz-progress]').textContent !== before",
|
|
arg=before,
|
|
)
|
|
assert "1 / 2 answered" in page.locator("[data-quiz-progress]").inner_text()
|
|
assert "2 / 3 points" in page.locator("[data-quiz-score]").inner_text()
|
|
|
|
|
|
def test_an_answered_question_cannot_be_resubmitted(alice):
|
|
page, _ = alice
|
|
slug = _author_quiz("Player once quiz")
|
|
_start(page, slug)
|
|
question = page.locator(".quiz-question-single_choice").first
|
|
question.locator("input[type='radio']").last.check()
|
|
question.locator("button:has-text('Submit answer')").click()
|
|
question.locator(".quiz-grade").wait_for(state="visible")
|
|
assert question.locator("button:has-text('Submit answer')").count() == 0
|
|
assert question.locator("input[type='radio']").first.is_disabled()
|
|
|
|
|
|
def test_a_numeric_question_takes_a_typed_answer(alice):
|
|
page, _ = alice
|
|
slug = _author_quiz("Player numeric quiz")
|
|
_start(page, slug)
|
|
question = page.locator(".quiz-question-numeric").first
|
|
question.locator("input[name='answer_text']").fill("1024")
|
|
question.locator("button:has-text('Submit answer')").click()
|
|
question.locator(".quiz-grade-correct").wait_for(state="visible")
|
|
|
|
|
|
def test_the_timer_is_rendered_for_a_limited_quiz(alice):
|
|
page, _ = alice
|
|
slug = _author_quiz("Player timer quiz", time_limit_seconds="600")
|
|
_start(page, slug)
|
|
timer = page.locator("[data-quiz-timer]")
|
|
timer.wait_for(state="visible")
|
|
assert timer.inner_text().strip()
|
|
|
|
|
|
def test_no_timer_is_rendered_without_a_limit(alice):
|
|
page, _ = alice
|
|
slug = _author_quiz("Player untimed quiz")
|
|
_start(page, slug)
|
|
page.locator(".quiz-hud").wait_for(state="visible")
|
|
assert page.locator("[data-quiz-timer]").count() == 0
|
|
|
|
|
|
def test_the_answer_key_is_not_in_the_page_source(alice):
|
|
page, _ = alice
|
|
slug = _author_quiz("Player hidden key quiz", questions=(NUMERIC,), reveal_answers="0")
|
|
_start(page, slug)
|
|
assert "1024" not in page.content()
|
|
|
|
|
|
def test_reloading_resumes_the_same_attempt(alice):
|
|
page, _ = alice
|
|
slug = _author_quiz("Player resume quiz")
|
|
_start(page, slug)
|
|
attempt_url = page.url
|
|
question = page.locator(".quiz-question-single_choice").first
|
|
question.locator("input[type='radio']").last.check()
|
|
question.locator("button:has-text('Submit answer')").click()
|
|
question.locator(".quiz-grade").wait_for(state="visible")
|
|
page.goto(f"{BASE_URL}/quizzes/{slug}", wait_until="domcontentloaded")
|
|
page.locator(".quiz-detail-actions a:has-text('Resume attempt')").click()
|
|
page.wait_for_url("**/attempts/**", wait_until="domcontentloaded")
|
|
assert page.url == attempt_url
|
|
assert page.locator(".quiz-question-answered").count() == 1
|
|
|
|
|
|
def test_finishing_reaches_the_results_page(alice):
|
|
page, _ = alice
|
|
slug = _author_quiz("Player finish quiz", pass_percent="50")
|
|
_start(page, slug)
|
|
for question in (".quiz-question-single_choice", ".quiz-question-numeric"):
|
|
node = page.locator(question).first
|
|
if "numeric" in question:
|
|
node.locator("input[name='answer_text']").fill("1024")
|
|
else:
|
|
node.locator("input[type='radio']").last.check()
|
|
node.locator("button:has-text('Submit answer')").click()
|
|
node.locator(".quiz-grade").wait_for(state="visible")
|
|
page.locator("form[data-quiz-finish-form] button").click()
|
|
page.locator(DIALOG_CONFIRM).wait_for(state="visible")
|
|
page.locator(DIALOG_CONFIRM).click()
|
|
page.wait_for_url("**/results", wait_until="domcontentloaded")
|
|
page.locator(".quiz-score-card").wait_for(state="visible")
|
|
assert "100" in page.locator(".quiz-score-percent").inner_text()
|
|
|
|
|
|
def test_the_results_page_shows_the_pass_verdict(alice):
|
|
page, _ = alice
|
|
slug = _author_quiz("Player verdict quiz", questions=(NUMERIC,), pass_percent="50")
|
|
_start(page, slug)
|
|
node = page.locator(".quiz-question-numeric").first
|
|
node.locator("input[name='answer_text']").fill("1024")
|
|
node.locator("button:has-text('Submit answer')").click()
|
|
node.locator(".quiz-grade").wait_for(state="visible")
|
|
page.locator("form[data-quiz-finish-form] button").click()
|
|
page.locator(DIALOG_CONFIRM).wait_for(state="visible")
|
|
page.locator(DIALOG_CONFIRM).click()
|
|
page.wait_for_url("**/results", wait_until="domcontentloaded")
|
|
page.locator(".quiz-verdict-pass").wait_for(state="visible")
|
|
|
|
|
|
def test_the_results_page_reviews_every_answer(alice):
|
|
page, _ = alice
|
|
slug = _author_quiz("Player review quiz", allow_review="1")
|
|
_start(page, slug)
|
|
node = page.locator(".quiz-question-single_choice").first
|
|
node.locator("input[type='radio']").last.check()
|
|
node.locator("button:has-text('Submit answer')").click()
|
|
node.locator(".quiz-grade").wait_for(state="visible")
|
|
page.locator("form[data-quiz-finish-form] button").click()
|
|
page.locator(DIALOG_CONFIRM).wait_for(state="visible")
|
|
page.locator(DIALOG_CONFIRM).click()
|
|
page.wait_for_url("**/results", wait_until="domcontentloaded")
|
|
page.locator(".quiz-review").wait_for(state="visible")
|
|
assert page.locator(".quiz-review-item").count() == 2
|
|
|
|
|
|
def test_the_review_is_hidden_when_the_author_disabled_it(alice):
|
|
page, _ = alice
|
|
slug = _author_quiz("Player no review quiz", questions=(NUMERIC,), allow_review="0")
|
|
_start(page, slug)
|
|
node = page.locator(".quiz-question-numeric").first
|
|
node.locator("input[name='answer_text']").fill("1024")
|
|
node.locator("button:has-text('Submit answer')").click()
|
|
node.locator(".quiz-grade").wait_for(state="visible")
|
|
page.locator("form[data-quiz-finish-form] button").click()
|
|
page.locator(DIALOG_CONFIRM).wait_for(state="visible")
|
|
page.locator(DIALOG_CONFIRM).click()
|
|
page.wait_for_url("**/results", wait_until="domcontentloaded")
|
|
page.locator(".quiz-review-disabled").wait_for(state="visible")
|
|
|
|
|
|
def test_the_results_page_offers_a_replay(alice):
|
|
page, _ = alice
|
|
slug = _author_quiz("Player replay quiz", questions=(NUMERIC,))
|
|
_start(page, slug)
|
|
node = page.locator(".quiz-question-numeric").first
|
|
node.locator("input[name='answer_text']").fill("1024")
|
|
node.locator("button:has-text('Submit answer')").click()
|
|
node.locator(".quiz-grade").wait_for(state="visible")
|
|
page.locator("form[data-quiz-finish-form] button").click()
|
|
page.locator(DIALOG_CONFIRM).wait_for(state="visible")
|
|
page.locator(DIALOG_CONFIRM).click()
|
|
page.wait_for_url("**/results", wait_until="domcontentloaded")
|
|
page.locator(".quiz-results-actions button:has-text('Play again')").click()
|
|
page.wait_for_url("**/attempts/**", wait_until="domcontentloaded")
|
|
page.locator("dp-quiz-player").wait_for(state="visible")
|
|
|
|
|
|
def test_the_hub_shows_the_completed_badge_afterwards(alice):
|
|
page, _ = alice
|
|
slug = _author_quiz("Player badge quiz", questions=(NUMERIC,))
|
|
_start(page, slug)
|
|
node = page.locator(".quiz-question-numeric").first
|
|
node.locator("input[name='answer_text']").fill("1024")
|
|
node.locator("button:has-text('Submit answer')").click()
|
|
node.locator(".quiz-grade").wait_for(state="visible")
|
|
page.locator("form[data-quiz-finish-form] button").click()
|
|
page.locator(DIALOG_CONFIRM).wait_for(state="visible")
|
|
page.locator(DIALOG_CONFIRM).click()
|
|
page.wait_for_url("**/results", wait_until="domcontentloaded")
|
|
page.goto(f"{BASE_URL}/quizzes?filter=done", wait_until="domcontentloaded")
|
|
card = page.locator(f".quiz-card:has(a[href='/quizzes/{slug}'])").first
|
|
card.wait_for(state="visible")
|
|
assert card.locator(".quiz-state-done").count() == 1
|