File size: 13,246 Bytes
0ba5932
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
"""Preparing for an exam: the exam names its units, and the student sees how ready they are and a plan of steps, with a
daily amount and each step checked off from their progress."""
import json
import socket
import threading
import time
from datetime import date, datetime, timedelta, timezone

import pytest

from app.main import create_app
from test_assignments import MemoryAssignments
from test_focus_browser import DANA, SchoolAuth, mail, session
from test_worksheets import MemoryWorksheets
from test_worksheets_browser import NoAnswers, uvicorn

playwright = pytest.importorskip("playwright.sync_api")


class ExamAuth(SchoolAuth):
    exams = []

    def classes(self, fresh=False):
        return [{**super().classes()[0], "exams": self.exams}]


@pytest.fixture
def exam_server():
    auth = ExamAuth()
    app = create_app(auth=auth)
    app.state.assignments, app.state.worksheets, app.state.answers = MemoryAssignments(), MemoryWorksheets(), NoAnswers()
    with socket.socket() as sock:
        sock.bind(("127.0.0.1", 0))
        server = uvicorn().Server(uvicorn().Config(app, log_level="error"))
        thread = threading.Thread(target=server.run, kwargs={"sockets": [sock]}, daemon=True)
        thread.start()
        try:
            while not server.started:
                time.sleep(.01)
            yield f"http://127.0.0.1:{sock.getsockname()[1]}", auth
        finally:
            server.should_exit = True
            thread.join(timeout=5)


def ago(days):
    return (datetime.now(timezone.utc) - timedelta(days=days)).isoformat()


# unit t: t_ext finished lately, t_simplify finished long ago (fading), t_ext_eq tried, t_area and t_formulas not started
PROGRESS = {"done": {"t_ext": ago(1), "t_simplify": ago(40)}, "seen": {"t_ext": ago(1), "t_simplify": ago(40), "t_ext_eq": ago(2)},
            "tries": {"t_ext_eq": 2}, "updated": 1}


def with_progress(page, progress):
    k = json.dumps("mathly.progress.v1:" + mail[DANA])   # only the first time: a reload keeps what the page saved
    page.add_init_script("if(!localStorage.getItem(%s)) localStorage.setItem(%s, %s)" % (k, k, json.dumps(json.dumps(progress))))


def test_the_student_sees_readiness_and_a_plan_with_a_daily_amount(exam_server):
    url, auth = exam_server
    auth.exams = [{"subject": "מתמטיקה", "date": (date.today() + timedelta(days=3)).isoformat(), "units": ["t"], "note": "פרק 3"}]
    with playwright.sync_playwright() as p:
        browser = p.chromium.launch()
        try:
            page, errors = session(browser, DANA)
            with_progress(page, PROGRESS)
            page.goto(url)
            page.locator('[data-subject="מתמטיקה"]').click()
            page.locator(".mx-prep-open").click()
            prep = page.locator(".mx-prep-sheet")
            playwright.expect(prep.locator("#mxTitle")).to_have_text("הכנה למבחן במתמטיקה")
            playwright.expect(prep).to_contain_text("חומר למבחן: טכניקה אלגברית 1")
            playwright.expect(prep).to_contain_text("פרק 3")
            playwright.expect(prep.locator(".mx-prep-sum")).to_have_text("2 מתוך 5 מיומנויות הושלמו · 1 דועכות")
            playwright.expect(prep.locator(".mx-prep-skills li")).to_have_count(5)
            playwright.expect(prep.locator('.mx-prep-skills li[data-state="fading"]')).to_have_count(1)
            steps = prep.locator(".mx-prep-step")
            playwright.expect(steps).to_have_count(8)   # 3 not finished, 1 fading, the unit's quiz, the three quizzes
            playwright.expect(prep.locator(".mx-prep-plan")).to_contain_text("3 צעדים ביום")
            playwright.expect(prep.locator(".mx-prep-step.today")).to_have_count(3)
            playwright.expect(steps.nth(0)).to_contain_text("ללמוד ולסיים")
            playwright.expect(steps.nth(5)).to_contain_text("מבחן תרגול 1")
            playwright.expect(steps.nth(6)).to_contain_text("מבחן תרגול 2")
            playwright.expect(steps.nth(7)).to_contain_text("מבחן תרגול כמו במבחן")
            # a step done from progress made after the plan was opened
            page.evaluate("""() => { const k = Object.keys(localStorage).find(k => k.startsWith('mathly.progress.v1:'));
                const P = JSON.parse(localStorage.getItem(k)); P.done.t_area = new Date().toISOString(); localStorage.setItem(k, JSON.stringify(P)); }""")
            page.reload()
            page.locator('[data-subject="מתמטיקה"]').click()
            page.locator(".mx-prep-open").click()
            playwright.expect(prep.locator(".mx-prep-step.done")).to_have_count(1)
            playwright.expect(prep.locator(".mx-prep-step.done")).to_contain_text("השוואת שטחים")
            # the first quiz's step starts it, over the exam's units only; handed in, the step is done
            prep.locator('[data-hq="s1"]').click()
            sheet = page.locator(".mx-quiz-sheet")
            playwright.expect(sheet.locator(".mx-uchip")).to_have_text("מבחן תרגול 1")
            n = sheet.locator(".mx-quiz-dots button").count()
            for i in range(n):
                sheet.locator(".mx-choices button[data-right]").first.click()
                sheet.locator("[data-act=quiz-submit]" if i == n - 1 else "[data-act=quiz-next]").click()
            playwright.expect(sheet.locator("#mxTitle")).to_have_text(f"{n} מתוך {n} נכונות")
            qs = page.evaluate("""() => { const k = Object.keys(localStorage).find(k => k.startsWith('mathly.progress.v1:'));
                return JSON.parse(localStorage.getItem(k)).quizzes.at(-1).qs; }""")
            assert qs and all(q.startswith("t_") for q in qs)
            sheet.locator(".mx-x").click()
            page.locator(".mx-prep-open").click()
            playwright.expect(prep.locator(".mx-prep-step.done", has_text="מבחן תרגול 1")).to_have_count(1)
            assert not errors, errors
        finally:
            browser.close()


def test_the_day_before_the_exam_the_plan_has_no_daily_amount(exam_server):
    url, auth = exam_server
    auth.exams = [{"subject": "מתמטיקה", "date": (date.today() + timedelta(days=1)).isoformat(), "units": ["t"]},
                  {"subject": "מתמטיקה", "date": (date.today() + timedelta(days=9)).isoformat()}]   # no units: no preparation
    with playwright.sync_playwright() as p:
        browser = p.chromium.launch()
        try:
            page, errors = session(browser, DANA)
            with_progress(page, PROGRESS)
            page.goto(url)
            page.locator('[data-subject="מתמטיקה"]').click()
            playwright.expect(page.locator(".mx-prep-open")).to_have_count(1)
            page.locator(".mx-prep-open").click()
            prep = page.locator(".mx-prep-sheet")
            playwright.expect(prep.locator(".mx-uchip")).to_have_text("מחר")
            playwright.expect(prep.locator(".mx-prep-step")).to_have_count(8)   # the three quizzes at the end
            playwright.expect(prep.locator(".mx-prep-plan")).not_to_contain_text("צעדים ביום")
            playwright.expect(prep.locator(".mx-prep-step.today")).to_have_count(0)
            assert not errors, errors
        finally:
            browser.close()


def test_the_teacher_names_the_units_and_sees_how_ready_the_class_is(exam_server):
    from test_focus_browser import TEACHER
    url, auth = exam_server
    auth.exams = [{"subject": "מתמטיקה", "date": (date.today() + timedelta(days=5)).isoformat()}]
    saved = []

    def save(class_id, exams):
        saved.append(exams)
        auth.exams = exams
    auth.save_class_exams = save
    with playwright.sync_playwright() as p:
        browser = p.chromium.launch()
        try:
            page, errors = session(browser, TEACHER)
            page.goto(url)
            page.locator('[data-act="class-report"]').click()
            sheet = page.locator(".mx-report-sheet")
            sheet.locator("[data-report-tab=exams]").click()
            form = sheet.locator("[data-exam-form]")
            playwright.expect(sheet.locator(".mx-exam-t")).to_contain_text("בחרו את היחידות של המבחן")
            form.locator('[name=unit][value="t"]').check()
            form.locator("[name=note]").fill("פרק 3")
            form.locator("button[type=submit]").click()
            playwright.expect(sheet.locator(".mx-report")).to_contain_text("החומר למבחן נשמר")
            assert saved[-1][0]["units"] == ["t"] and saved[-1][0]["note"] == "פרק 3"
            playwright.expect(sheet.locator(".mx-exam-t .mx-report-table tbody tr")).to_have_count(5)   # the 5 skills of t
            playwright.expect(sheet.locator(".mx-exam-t")).to_contain_text("הכי רחוקים מהחומר")
            assert not errors, errors
        finally:
            browser.close()


def test_a_plan_saved_with_the_old_practice_exam_step_gets_the_three_quizzes(exam_server):
    url, auth = exam_server
    when = (date.today() + timedelta(days=5)).isoformat()
    auth.exams = [{"subject": "מתמטיקה", "date": when, "units": ["t"], "note": ""}]
    old = {**PROGRESS, "prep": {f"3|מתמטיקה|{when}": {"opened": ago(1), "steps": [{"k": "skill", "id": "t_area"}, {"k": "exam"}]}}}
    with playwright.sync_playwright() as p:
        browser = p.chromium.launch()
        try:
            page, errors = session(browser, DANA)
            with_progress(page, old)
            page.goto(url)
            page.locator('[data-subject="מתמטיקה"]').click()
            page.locator(".mx-prep-open").click()
            steps = page.locator(".mx-prep-sheet .mx-prep-step")
            playwright.expect(steps).to_have_count(4)
            playwright.expect(steps.nth(1)).to_contain_text("מבחן תרגול 1")
            playwright.expect(steps.nth(3)).to_contain_text("מבחן תרגול כמו במבחן")
            assert not errors, errors
        finally:
            browser.close()


def test_home_highlights_the_lessons_to_finish_before_the_next_exam(exam_server):
    url, auth = exam_server
    auth.exams = [{"subject": "מתמטיקה", "date": (date.today() + timedelta(days=4)).isoformat(), "units": ["t", "k"]}]
    with playwright.sync_playwright() as p:
        browser = p.chromium.launch()
        try:
            page, errors = session(browser, DANA)
            with_progress(page, {**PROGRESS, "done": {**PROGRESS["done"], **{s: ago(1) for s in ("k_def", "k_angles", "k_diag", "k_axes")}}})
            page.goto(url)
            page.locator('[data-subject="מתמטיקה"]').click()
            page.locator('.mx-nav [data-view="home"]').click()
            lessons = page.locator(".mx-home-lessons")
            playwright.expect(lessons.locator("h3")).to_have_text("להשלים לפני המבחן: שיעור אחד")
            playwright.expect(lessons.locator("li.todo")).to_have_count(1)                # unit t: 2 of 5 skills
            playwright.expect(lessons.locator("li.todo")).to_contain_text("2 מתוך 5 מיומנויות")
            playwright.expect(lessons.locator("li.done")).to_contain_text("✓ הושלם")       # unit k: all done
            lessons.locator("li.todo button").click()                                    # opens the lesson on the path
            playwright.expect(page.locator('.mx-nav [data-view="path"]')).to_have_attribute("aria-current", "page")
            assert not errors, errors
        finally:
            browser.close()


def test_the_teacher_adds_and_deletes_an_exam_on_the_classs_table(exam_server):
    from test_focus_browser import TEACHER
    url, auth = exam_server
    auth.exams = []

    def save(class_id, exams):
        auth.exams = exams
    auth.save_class_exams = save
    day = (date.today() + timedelta(days=12)).isoformat()
    with playwright.sync_playwright() as p:
        browser = p.chromium.launch()
        try:
            page, errors = session(browser, TEACHER)
            page.on("dialog", lambda d: d.accept())
            page.goto(url)
            page.locator('[data-act="class-report"]').click()
            sheet = page.locator(".mx-report-sheet")
            sheet.locator("[data-report-tab=exams]").click()
            playwright.expect(sheet.locator(".mx-report")).to_contain_text("אין מבחנים קרובים")
            form = sheet.locator("[data-exam-add]")
            form.locator("[name=date]").fill(day)
            form.locator('[name=unit][value="t"]').check()
            form.locator("[name=note]").fill("פרק 3")
            form.locator("button[type=submit]").click()
            playwright.expect(sheet.locator(".mx-flash")).to_contain_text("נוסף ללוח")
            playwright.expect(sheet.locator(".mx-exam-t h4")).to_contain_text("מתמטיקה")
            assert auth.exams == [{"subject": "מתמטיקה", "date": day, "units": ["t"], "note": "פרק 3"}]
            sheet.locator("[data-exam-del]").click()
            playwright.expect(sheet.locator(".mx-flash")).to_contain_text("נמחק")
            assert auth.exams == []
            assert not errors, errors
        finally:
            browser.close()