mathly / tests /test_home_browser.py
3v324v23's picture
Add tests for student task list and quiz modes functionality
0ba5932
Raw History Blame Contribute Delete
8.62 kB
"""Home and path tabs (openspec student-home-and-exam-quizzes): home shows only what has a date or needs the student,
the path tab holds the self-paced learning, and a class teacher keeps their classes."""
from datetime import date, datetime, timedelta, timezone
import pytest
from test_exam_prep_browser import ExamAuth, exam_server, with_progress # noqa: F401 (a fixture)
from test_focus_browser import DANA, TEACHER, session
playwright = pytest.importorskip("playwright.sync_api")
COMMENT = {"attempt_id": "a1", "question": "驻讬诇讜讙 诪讜专讞讘", "score": 80, "question_id": "t_ext"}
def days_from_now(n):
return (date.today() + timedelta(days=n)).isoformat()
def to_math(page):
"""With exams the app asks for the subject first; without, it goes straight to math."""
page.locator('[data-subject="诪转诪讟讬拽讛"], #mathView .mx-top').first.wait_for()
if page.locator('[data-subject="诪转诪讟讬拽讛"]').count():
page.locator('[data-subject="诪转诪讟讬拽讛"]').click()
def open_home(browser, url, comments=()):
page, errors = session(browser, DANA)
page.route("**/api/math/teacher-comments", lambda route: route.fulfill(json={"comments": list(comments)}))
def me(route): # comments are loaded only where answer scans are on
reply = route.fetch()
route.fulfill(response=reply, json={**reply.json(), "answer_scans": True})
page.route("**/api/me", me)
page.goto(url)
to_math(page)
return page, errors
def test_home_shows_what_is_due_in_order_and_no_trail(exam_server):
url, auth = exam_server
auth.exams = [{"subject": "诪转诪讟讬拽讛", "date": days_from_now(5), "units": ["t"], "note": ""}]
with playwright.sync_playwright() as p:
browser = p.chromium.launch()
try:
page, errors = open_home(browser, url, [COMMENT])
nav = page.locator(".mx-nav button")
playwright.expect(nav.locator("span")).to_have_text(["讘讬转", "诪住诇讜诇", "爪讬讜谞讬诐", "讛讗讜住祝"])
playwright.expect(page.locator('.mx-nav [data-view="home"]')).to_have_attribute("aria-current", "page")
home = page.locator(".mx-home")
exam = home.locator(".mx-home-exam")
playwright.expect(exam).to_contain_text("诪讘讞谉 讘诪转诪讟讬拽讛")
playwright.expect(exam).to_contain_text("注讜讚 5 讬诪讬诐")
playwright.expect(page.locator(".mx-teacher-comment")).to_contain_text("驻讬诇讜讙 诪讜专讞讘")
playwright.expect(page.locator(".mx-continue")).to_be_visible()
# the order: exam, quizzes, due work, comments, then the way to the path
order = page.evaluate("""() => [...document.querySelector('.mx-home').children].map(el =>
el.matches('.mx-home-exam') ? 'exam' : el.matches('.mx-hq') ? 'quiz'
: el.matches('.mx-tasks-sum, .mx-grades-btn, .mx-asgs') ? 'due' : el.matches('.mx-teacher-comment') ? 'comment'
: el.matches('.mx-continue') ? 'continue' : el.className)""")
assert order.index("exam") < order.index("quiz") < order.index("due") < order.index("comment") < order.index("continue"), order
# nothing of the path on home
for sel in (".mx-trail", ".mx-daily", ".mx-review", ".mx-patterns"):
playwright.expect(page.locator(sel)).to_have_count(0)
# the exam opens its preparation
exam.click()
playwright.expect(page.locator(".mx-prep-sheet #mxTitle")).to_have_text("讛讻谞讛 诇诪讘讞谉 讘诪转诪讟讬拽讛")
assert not errors, errors
finally:
browser.close()
def test_seen_comments_are_not_on_home_and_an_exam_without_units_has_no_preparation(exam_server):
url, auth = exam_server
auth.exams = [{"subject": "诪转诪讟讬拽讛", "date": days_from_now(9)}]
with playwright.sync_playwright() as p:
browser = p.chromium.launch()
try:
page, errors = open_home(browser, url)
exam = page.locator(".mx-home-exam")
playwright.expect(exam).to_contain_text("注讜讚 9 讬诪讬诐")
playwright.expect(exam.locator("[data-prep]")).to_have_count(0)
assert exam.evaluate("el => el.tagName") == "DIV" # nothing to open
playwright.expect(page.locator(".mx-teacher-comment")).to_have_count(0)
assert not errors, errors
finally:
browser.close()
def open_questions_only(route):
"""The skills with no multiple-choice question: no quiz can be offered."""
graph = route.fetch().json()
for skill in graph["skills"]:
for q in skill.get("questions", []):
q.pop("choices", None)
route.fulfill(json=graph)
def test_home_is_never_empty_and_continue_opens_the_path_at_the_next_topic(exam_server):
url, auth = exam_server
auth.exams = [] # no exam, no quiz, nothing due
with playwright.sync_playwright() as p:
browser = p.chromium.launch()
try:
page, errors = session(browser, DANA)
page.route("**/api/math/skills", open_questions_only)
page.goto(url)
to_math(page)
playwright.expect(page.locator(".mx-home-empty")).to_have_text("讗讬谉 讻专讙注 诪砖讛讜 诇讛讙讬砖 讗讜 诪讘讞谉 拽专讜讘.")
playwright.expect(page.locator(".mx-continue")).to_be_visible()
assert not errors, errors
finally:
browser.close()
with playwright.sync_playwright() as p:
browser = p.chromium.launch()
try:
page, errors = open_home(browser, url)
playwright.expect(page.locator(".mx-home-empty")).to_have_count(0) # a quiz is offered
page.locator(".mx-continue").click()
playwright.expect(page.locator('.mx-nav [data-view="path"]')).to_have_attribute("aria-current", "page")
playwright.expect(page.locator(".mx-star.st-next")).to_be_in_viewport()
assert not errors, errors
finally:
browser.close()
def ago(days):
return (datetime.now(timezone.utc) - timedelta(days=days)).isoformat()
def test_the_path_tab_holds_review_and_the_collection_jumps_to_a_unit(exam_server):
url, auth = exam_server
auth.exams = []
old = ago(30)
due = {"t_ext": old, "t_simplify": old, "t_ext_eq": old}
with playwright.sync_playwright() as p:
browser = p.chromium.launch()
try:
page, errors = session(browser, DANA)
with_progress(page, {"done": due, "seen": due, "review": {k: {"box": 0, "due": "2020-01-01"} for k in due}, "updated": 1})
page.goto(url)
to_math(page)
playwright.expect(page.locator(".mx-review")).to_have_count(0) # not on home
page.locator('.mx-nav [data-view="path"]').click()
playwright.expect(page.locator(".mx-review")).to_be_visible()
playwright.expect(page.locator(".mx-daily")).to_be_visible()
# path order: daily goal, review, then the trail
order = page.evaluate("""() => ['.mx-daily', '.mx-review', '.mx-trail'].map(s =>
[...document.querySelectorAll('#mathView *')].indexOf(document.querySelector(s)))""")
assert order == sorted(order) and -1 not in order, order
page.locator('.mx-nav [data-view="sky"]').click()
page.locator('.mx-con-card[data-unit="k"]').click()
playwright.expect(page.locator('.mx-nav [data-view="path"]')).to_have_attribute("aria-current", "page")
playwright.expect(page.locator("#mx-unit-k")).to_be_in_viewport()
assert not errors, errors
finally:
browser.close()
def test_a_class_teacher_keeps_the_classes_and_has_no_path_tab(exam_server):
url, auth = exam_server
with playwright.sync_playwright() as p:
browser = p.chromium.launch()
try:
page, errors = session(browser, TEACHER)
page.goto(url)
to_math(page)
playwright.expect(page.locator(".mx-classes")).to_be_visible()
playwright.expect(page.locator('.mx-nav [data-view="path"]')).to_have_count(0)
# the student preview has both tabs
page.locator(".mx-myclass").first.click()
page.locator('[data-act="preview-on"]').click()
to_math(page)
playwright.expect(page.locator('.mx-nav [data-view="path"]')).to_have_count(1)
playwright.expect(page.locator(".mx-continue")).to_be_visible()
assert not errors, errors
finally:
browser.close()