|
# retoor <retoor@molodetz.nl>
|
|
|
|
import requests
|
|
|
|
from tests.conftest import BASE_URL
|
|
|
|
JSON = {"Accept": "application/json"}
|
|
|
|
LAYOUT = ".feed-layout"
|
|
LEFT = ".feed-layout > .sidebar-card"
|
|
CENTRE = ".feed-layout > .feed-main"
|
|
RIGHT = ".feed-layout > .feed-right"
|
|
NAV_ENTRY = ".topnav-link[href='/quizzes']"
|
|
NEW_QUIZ_BUTTON = ".quiz-actions a[href='/quizzes/new']"
|
|
DEVII_BUTTON = ".quiz-actions button[data-devii-open]"
|
|
SCOREBOARD = ".quiz-scoreboard-card"
|
|
|
|
|
|
def _seed_quiz(title="Hub e2e quiz"):
|
|
session = requests.Session()
|
|
name = f"hub{abs(hash(title)) % 10**9}"
|
|
session.post(
|
|
f"{BASE_URL}/auth/signup",
|
|
data={
|
|
"username": name,
|
|
"email": f"{name}@t.dev",
|
|
"password": "secret123",
|
|
"confirm_password": "secret123",
|
|
},
|
|
allow_redirects=True,
|
|
)
|
|
slug = session.post(
|
|
f"{BASE_URL}/quizzes/create", data={"title": title, "description": "d"}, headers=JSON
|
|
).json()["data"]["slug"]
|
|
session.post(
|
|
f"{BASE_URL}/quizzes/{slug}/questions",
|
|
data={
|
|
"kind": "single_choice",
|
|
"prompt": "Which journal mode allows concurrent readers?",
|
|
"points": 2,
|
|
"options": "DELETE\nWAL",
|
|
"correct_indexes": "1",
|
|
},
|
|
headers=JSON,
|
|
)
|
|
session.post(f"{BASE_URL}/quizzes/{slug}/publish", headers=JSON)
|
|
return slug
|
|
|
|
|
|
def test_the_hub_renders_the_three_column_layout(page, app_server):
|
|
_seed_quiz("Layout e2e quiz")
|
|
page.goto(f"{BASE_URL}/quizzes", wait_until="domcontentloaded")
|
|
page.locator(LAYOUT).wait_for(state="visible")
|
|
assert page.locator(LEFT).count() == 1
|
|
assert page.locator(CENTRE).count() == 1
|
|
assert page.locator(RIGHT).count() == 1
|
|
|
|
|
|
def test_the_hub_has_a_visible_heading(page, app_server):
|
|
page.goto(f"{BASE_URL}/quizzes", wait_until="domcontentloaded")
|
|
heading = page.locator("h1.quiz-page-title")
|
|
heading.wait_for(state="visible")
|
|
assert heading.inner_text().strip() == "Quizzes"
|
|
|
|
|
|
def test_the_nav_entry_is_visible_to_a_guest_and_active(page, app_server):
|
|
page.goto(f"{BASE_URL}/quizzes", wait_until="domcontentloaded")
|
|
entry = page.locator(NAV_ENTRY)
|
|
entry.wait_for(state="visible")
|
|
assert "active" in (entry.get_attribute("class") or "")
|
|
|
|
|
|
def test_a_guest_sees_disabled_create_controls(page, app_server):
|
|
page.goto(f"{BASE_URL}/quizzes", wait_until="domcontentloaded")
|
|
page.locator(".quiz-actions").wait_for(state="visible")
|
|
assert page.locator(NEW_QUIZ_BUTTON).get_attribute("aria-disabled") == "true"
|
|
assert page.locator(DEVII_BUTTON).is_disabled()
|
|
|
|
|
|
def test_a_guest_still_sees_the_list_and_the_scoreboard(page, app_server):
|
|
_seed_quiz("Guest visible quiz")
|
|
page.goto(f"{BASE_URL}/quizzes", wait_until="domcontentloaded")
|
|
page.locator(SCOREBOARD).wait_for(state="visible")
|
|
assert page.locator(".quiz-card").count() >= 1
|
|
|
|
|
|
def test_a_member_sees_enabled_create_controls(alice):
|
|
page, _ = alice
|
|
page.goto(f"{BASE_URL}/quizzes", wait_until="domcontentloaded")
|
|
page.locator(NEW_QUIZ_BUTTON).wait_for(state="visible")
|
|
assert page.locator(NEW_QUIZ_BUTTON).get_attribute("aria-disabled") is None
|
|
assert page.locator(DEVII_BUTTON).is_enabled()
|
|
|
|
|
|
def test_a_member_sees_the_personal_filters(alice):
|
|
page, _ = alice
|
|
page.goto(f"{BASE_URL}/quizzes", wait_until="domcontentloaded")
|
|
page.locator(".sidebar-nav").wait_for(state="visible")
|
|
for target in ("todo", "done", "mine", "drafts"):
|
|
assert page.locator(f".sidebar-link[href='/quizzes?filter={target}']").count() == 1
|
|
|
|
|
|
def test_a_guest_sees_only_the_all_filter(page, app_server):
|
|
page.goto(f"{BASE_URL}/quizzes", wait_until="domcontentloaded")
|
|
page.locator(".sidebar-nav").wait_for(state="visible")
|
|
assert page.locator(".sidebar-link[href='/quizzes?filter=todo']").count() == 0
|
|
assert page.locator(".sidebar-link[href='/quizzes?filter=all']").count() == 1
|
|
|
|
|
|
def test_a_quiz_card_shows_its_state_badge(alice):
|
|
page, _ = alice
|
|
slug = _seed_quiz("Badge e2e quiz")
|
|
page.goto(f"{BASE_URL}/quizzes", 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-todo").count() == 1
|
|
|
|
|
|
def test_a_quiz_card_shows_its_meta_chips(page, app_server):
|
|
slug = _seed_quiz("Meta chips e2e quiz")
|
|
page.goto(f"{BASE_URL}/quizzes", wait_until="domcontentloaded")
|
|
card = page.locator(f".quiz-card:has(a[href='/quizzes/{slug}'])").first
|
|
card.wait_for(state="visible")
|
|
assert "1 questions" in card.inner_text()
|
|
assert "2 points" in card.inner_text()
|
|
|
|
|
|
def test_the_search_box_filters_the_list(page, app_server):
|
|
slug = _seed_quiz("Unmistakable pelican quiz")
|
|
page.goto(f"{BASE_URL}/quizzes?search=pelican", wait_until="domcontentloaded")
|
|
page.locator(".feed-posts").wait_for(state="visible")
|
|
assert page.locator(f".quiz-card:has(a[href='/quizzes/{slug}'])").count() == 1
|
|
|
|
|
|
def test_the_new_quiz_button_opens_the_create_form(alice):
|
|
page, _ = alice
|
|
page.goto(f"{BASE_URL}/quizzes", wait_until="domcontentloaded")
|
|
page.locator(NEW_QUIZ_BUTTON).click()
|
|
page.wait_for_url("**/quizzes/new", wait_until="domcontentloaded")
|
|
page.locator("form.quiz-form").wait_for(state="visible")
|
|
|
|
|
|
def test_the_devii_button_seeds_the_terminal_without_sending(alice):
|
|
page, _ = alice
|
|
page.goto(f"{BASE_URL}/quizzes", wait_until="domcontentloaded")
|
|
trigger = page.locator(DEVII_BUTTON)
|
|
trigger.wait_for(state="visible")
|
|
prompt = trigger.get_attribute("data-devii-prompt")
|
|
assert prompt and "import_quiz" in prompt
|
|
trigger.click()
|
|
terminal_input = page.locator("devii-terminal .devii-input")
|
|
terminal_input.wait_for(state="visible", timeout=10000)
|
|
assert terminal_input.input_value() == prompt
|
|
assert page.locator("devii-terminal .devii-line-user").count() == 0
|
|
page.evaluate("window.app && window.app.devii && window.app.devii.close()")
|
|
page.evaluate("window.localStorage.clear()")
|
|
|
|
|
|
def test_opening_a_card_reaches_the_detail_page(page, app_server):
|
|
slug = _seed_quiz("Card link e2e quiz")
|
|
page.goto(f"{BASE_URL}/quizzes", wait_until="domcontentloaded")
|
|
page.locator(f".quiz-card a.card-link[href='/quizzes/{slug}']").first.click()
|
|
page.wait_for_url(f"**/quizzes/{slug}", wait_until="domcontentloaded")
|
|
page.locator("h1.quiz-detail-title").wait_for(state="visible")
|
|
|
|
|
|
def test_the_detail_page_shows_the_stats_strip(page, app_server):
|
|
slug = _seed_quiz("Stats strip e2e quiz")
|
|
page.goto(f"{BASE_URL}/quizzes/{slug}", wait_until="domcontentloaded")
|
|
page.locator(".quiz-stats-strip").wait_for(state="visible")
|
|
assert page.locator(".quiz-stat").count() == 5
|
|
|
|
|
|
def test_the_detail_page_offers_a_start_button_to_a_member(alice):
|
|
page, _ = alice
|
|
slug = _seed_quiz("Start button e2e quiz")
|
|
page.goto(f"{BASE_URL}/quizzes/{slug}", wait_until="domcontentloaded")
|
|
button = page.locator(".quiz-detail-actions button:has-text('Start quiz')")
|
|
button.wait_for(state="visible")
|
|
assert button.is_enabled()
|
|
|
|
|
|
def test_the_detail_page_disables_start_for_a_guest(page, app_server):
|
|
slug = _seed_quiz("Guest start e2e quiz")
|
|
page.goto(f"{BASE_URL}/quizzes/{slug}", wait_until="domcontentloaded")
|
|
button = page.locator(".quiz-detail-actions button:has-text('Start quiz')")
|
|
button.wait_for(state="visible")
|
|
assert button.is_disabled()
|
|
|
|
|
|
def test_the_detail_page_carries_a_comment_section(page, app_server):
|
|
slug = _seed_quiz("Comment section e2e quiz")
|
|
page.goto(f"{BASE_URL}/quizzes/{slug}", wait_until="domcontentloaded")
|
|
page.locator(".comments-section").wait_for(state="visible")
|