Student page with a standing summary, a class-comparison histogram (zero-dependency HTML/CSS) with a plain-language fewer/same/more sentence, opt-out management with withdrawal of future dates, and a call history that respects the per-course assessment-visibility setting and never shows pending or skipped calls. Opt-outs validate against a new per-course class-day schedule (range generator plus individual add/remove for holidays), since Canvas has no structured meeting-day data; courses without a schedule fall back to a free date picker. Dev mode seeds a Tue/Thu pattern. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
79 lines
2.3 KiB
Python
79 lines
2.3 KiB
Python
import datetime
|
|
import re
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.fixture
|
|
def instructor(dev_client):
|
|
dev_client.post("/dev/launch/dev-instructor")
|
|
return dev_client
|
|
|
|
|
|
def test_generate_range_creates_weekday_dates(instructor):
|
|
# A fixed two-week window well in the future, Mon/Wed.
|
|
resp = instructor.post(
|
|
"/instructor/schedule/generate",
|
|
data={
|
|
"start": "2027-01-04",
|
|
"end": "2027-01-15",
|
|
"weekday": ["0", "2"],
|
|
},
|
|
follow_redirects=True,
|
|
)
|
|
page = resp.get_data(as_text=True)
|
|
for expected in ["Jan 4", "Jan 6", "Jan 11", "Jan 13"]:
|
|
assert expected in page
|
|
assert "Jan 5" not in page
|
|
|
|
# Re-generating the same range adds nothing new.
|
|
resp = instructor.post(
|
|
"/instructor/schedule/generate",
|
|
data={"start": "2027-01-04", "end": "2027-01-15", "weekday": ["0"]},
|
|
follow_redirects=True,
|
|
)
|
|
assert resp.get_data(as_text=True).count("Jan 4") == 1
|
|
|
|
|
|
def test_generate_rejects_bad_input(instructor):
|
|
resp = instructor.post(
|
|
"/instructor/schedule/generate",
|
|
data={"start": "2027-01-15", "end": "2027-01-04", "weekday": ["0"]},
|
|
)
|
|
assert resp.status_code == 400
|
|
resp = instructor.post(
|
|
"/instructor/schedule/generate",
|
|
data={"start": "2027-01-04", "end": "2027-01-15"},
|
|
)
|
|
assert resp.status_code == 400
|
|
|
|
|
|
def test_add_and_delete_single_day(instructor):
|
|
resp = instructor.post(
|
|
"/instructor/schedule/add",
|
|
data={"date": "2027-03-01"},
|
|
follow_redirects=True,
|
|
)
|
|
page = resp.get_data(as_text=True)
|
|
assert "Mar 1, 2027" in page
|
|
|
|
# Find the delete form id adjacent to the date row.
|
|
rows = re.findall(
|
|
r"<td[^>]*>\s*(\w{3} \w{3} \d+, \d{4})\s*</td>.*?/instructor/schedule/(\d+)/delete",
|
|
page,
|
|
re.S,
|
|
)
|
|
ids = {label: did for label, did in rows}
|
|
day_id = ids["Mon Mar 1, 2027"]
|
|
|
|
resp = instructor.post(
|
|
f"/instructor/schedule/{day_id}/delete", follow_redirects=True
|
|
)
|
|
assert "Mar 1, 2027" not in resp.get_data(as_text=True)
|
|
|
|
|
|
def test_schedule_requires_instructor(dev_client):
|
|
dev_client.post("/dev/launch/dev-instructor") # create course
|
|
dev_client.post("/dev/launch/dev-student-1")
|
|
assert dev_client.get("/instructor/schedule").status_code == 403
|