# retoor 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