Everyone has the same buttons; the view is personal
The host's rights and the co-host are gone: anyone in a session may start a new page, put an image on the page, load a kept page, hide, expand and shrink, and move or remove images and Hides; the page left is kept in every notebook. Fit and Normal scale act on your own screen only. A session goes on without whoever started it, and a newcomer to an empty room starts page 1 with New. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
5d63a0f758
commit
60ad747e32
10 files changed
+215
-354
No files matched your search
+114
-106
@@ -136,7 +136,7 @@ PAGE_STATE = """
|
||||
hash: one.hash, x: one.x, y: one.y, width: one.width, height: one.height,
|
||||
ready: one.data !== null, shown: i < shown.length && !shown[i].hidden})),
|
||||
participants: session.participantList().map(
|
||||
(p) => ({name: p.name, host: p.host, present: p.self || p.peer !== null})),
|
||||
(p) => ({name: p.name, present: p.self || p.peer !== null})),
|
||||
layers,
|
||||
texts,
|
||||
};
|
||||
@@ -265,14 +265,6 @@ PEN_WAVE = """
|
||||
page.setPointerCapture = capture;
|
||||
"""
|
||||
|
||||
# Clicks the host's "Co-host" box beside the named participant.
|
||||
TOGGLE_PERMISSION = """
|
||||
const find = () => [...document.querySelectorAll('#participants li')]
|
||||
.find((li) => li.textContent.includes(arguments[0])).querySelector('.allow');
|
||||
find().click();
|
||||
return String(find().checked); // The list is rebuilt by the click.
|
||||
"""
|
||||
|
||||
# Makes an image of the given size in pixels and puts it on the page, as
|
||||
# the "Image" button does with a file. Arguments: width, height, kind
|
||||
# ('png' or 'jpeg'), a resolution in dots per inch to record in the file,
|
||||
@@ -799,7 +791,10 @@ class EndToEndTest(unittest.TestCase):
|
||||
|
||||
def test_browser_guest(self):
|
||||
"""A guest with nothing installed: the host's invitation is a link to
|
||||
the Kokkoja web site, and the guest's notebook is in the browser."""
|
||||
the Kokkoja web site, and the guest's notebook is in the browser.
|
||||
Everyone has every button (2026-10-18): the guest puts an image on
|
||||
the page, goes on without the host, and a newcomer to an empty
|
||||
room starts page 1."""
|
||||
firefox = self.firefox
|
||||
today = time.strftime("%Y_%m_%d")
|
||||
|
||||
@@ -959,14 +954,11 @@ class EndToEndTest(unittest.TestCase):
|
||||
self.assertTrue(picture.startswith(b"\x89PNG"))
|
||||
self.assertIn("downloads", firefox.run("return document.getElementById('notice').textContent;"))
|
||||
|
||||
# Allowed by the host, the guest puts an image on the page (the
|
||||
# file would come from the browser's own chooser).
|
||||
self.assertTrue(firefox.run("return document.getElementById('new_page').hidden;"))
|
||||
firefox.switch_to(host_tab)
|
||||
self.assertEqual(firefox.run(TOGGLE_PERMISSION, "Guest"), "true")
|
||||
firefox.switch_to(guest_tab)
|
||||
self.wait_until(lambda s: not firefox.run("return document.getElementById('new_page').hidden;"),
|
||||
"the guest to be allowed to start pages")
|
||||
# The guest puts an image on the page (the file would come from the
|
||||
# browser's own chooser); nobody's leave is needed (2026-10-18).
|
||||
self.assertFalse(firefox.run("return document.getElementById('new_page').hidden;"))
|
||||
self.assertFalse(firefox.run("return document.getElementById('load_image').hidden;"))
|
||||
self.assertIsNone(firefox.run("return document.querySelector('#participants .allow');"), "no Co-host box")
|
||||
firefox.run_until_done(LOAD_IMAGE, 300, 400, "png", 0, False)
|
||||
guest = self.wait_until(lambda s: s["image"] and s["image"]["ready"], "the guest's image")
|
||||
firefox.switch_to(host_tab)
|
||||
@@ -985,9 +977,34 @@ class EndToEndTest(unittest.TestCase):
|
||||
self.assertEqual(reloaded["layers"]["Guest"], guest["layers"]["Guest"])
|
||||
self.assertEqual(len(reloaded["participants"]), 2)
|
||||
|
||||
# The host leaves the session; the tabs are closed for the next test.
|
||||
# The host leaves; the guest goes on, and starts a new page without
|
||||
# anyone's leave. A newcomer to the room with nobody left in it
|
||||
# has no page, and New gives page 1; between two different pages
|
||||
# of one number the greater id wins, on every machine alike.
|
||||
firefox.switch_to(host_tab)
|
||||
firefox.go("about:blank")
|
||||
firefox.switch_to(guest_tab)
|
||||
self.wait_until(lambda s: [p["present"] for p in s["participants"]] == [True, False], "the host to be away")
|
||||
firefox.run("document.getElementById('new_page').click();")
|
||||
self.wait_until(lambda s: s["page"]["number"] == 2, "the guest's new page")
|
||||
firefox.in_session("DELETE", "/window")
|
||||
firefox.switch_to(host_tab)
|
||||
firefox.go(self.address("Late", join=urllib.parse.unquote(link.split("#join=")[1])))
|
||||
self.enter_name("Latecomer", "join_session")
|
||||
self.wait_until(lambda s: s["status"] == "Connected", "the latecomer to connect")
|
||||
time.sleep(0.5)
|
||||
self.assertEqual(self.state()["page"]["number"], 0)
|
||||
self.assertIn("New starts one", firefox.run("return document.getElementById('notice').textContent;"))
|
||||
firefox.run("document.getElementById('new_page').click();")
|
||||
self.wait_until(lambda s: s["page"]["number"] == 1, "the latecomer's page 1")
|
||||
tie = lambda id: firefox.run("""
|
||||
const session = window.kokkoja.session;
|
||||
session.considerPage({...session.page.descriptor(), id: arguments[0]}, 0);
|
||||
return [session.page.number, session.page.id];
|
||||
""", id)
|
||||
own = firefox.run("return window.kokkoja.session.page.id;")
|
||||
self.assertEqual(tie("-" * 22), [1, own], "a lesser id loses")
|
||||
self.assertEqual(tie("z" * 22), [1, "z" * 22], "a greater id wins")
|
||||
firefox.go("about:blank")
|
||||
|
||||
def test_tooltips(self):
|
||||
@@ -1086,7 +1103,8 @@ class EndToEndTest(unittest.TestCase):
|
||||
|
||||
def test_hide_rectangles(self):
|
||||
"""The teacher's requests of 2026-10-08: a Hide rectangle covers part
|
||||
of the page in white for everyone, marked by blue corners."""
|
||||
of the page in white for everyone, marked by blue corners. Everyone
|
||||
has the tool and may move or remove a Hide (2026-10-18)."""
|
||||
firefox = self.firefox
|
||||
host_tab = firefox.current_tab()
|
||||
firefox.go(self.address("Late"))
|
||||
@@ -1096,7 +1114,7 @@ class EndToEndTest(unittest.TestCase):
|
||||
firefox.go(self.address("Student", join=invitation))
|
||||
self.enter_name("Watcher", "join_session")
|
||||
self.wait_until(lambda s: s["page"]["number"] == 1 and len(s["participants"]) == 2, "the watcher")
|
||||
self.assertTrue(firefox.run("return document.querySelector('[data-tool=hide]').hidden;"))
|
||||
self.assertFalse(firefox.run("return document.querySelector('[data-tool=hide]').hidden;"))
|
||||
firefox.switch_to(host_tab)
|
||||
self.assertFalse(firefox.run("return document.querySelector('[data-tool=hide]').hidden;"))
|
||||
|
||||
@@ -1121,8 +1139,9 @@ class EndToEndTest(unittest.TestCase):
|
||||
self.assertFalse(white_at(hide[0] + hide[2] + 100, hide[1] + hide[3] + 100))
|
||||
|
||||
# The watcher sees it; hiding the host's layer on the watcher's
|
||||
# screen does not uncover it; the watcher can neither move nor
|
||||
# remove it.
|
||||
# screen does not uncover it. The watcher moves it by a hold, as
|
||||
# anyone may (2026-10-18), and takes the move back with Undo; the
|
||||
# host sees both.
|
||||
firefox.switch_to(guest_tab)
|
||||
self.wait_until(lambda s: s["hides"] == made["hides"], "the Hide to reach the watcher")
|
||||
firefox.run("""
|
||||
@@ -1134,9 +1153,16 @@ class EndToEndTest(unittest.TestCase):
|
||||
origin = self.state()["origin"]
|
||||
firefox.run_until_done(PEN_HOLD, (hide[0] + 50 - origin[0]) * vscale, (hide[1] + 50 - origin[1]) * vscale,
|
||||
100 * vscale, 0, 800)
|
||||
firefox.run(ERASER_AT, hide[0] + 50, hide[1] + 50, 5)
|
||||
time.sleep(0.5)
|
||||
self.assertEqual(self.state()["hides"], made["hides"])
|
||||
moved_by_watcher = [hide[0] + 100, *hide[1:]]
|
||||
self.wait_until(lambda s: s["hides"] == [moved_by_watcher], "the Hide moved by the watcher")
|
||||
firefox.switch_to(host_tab)
|
||||
self.wait_until(lambda s: s["hides"] == [moved_by_watcher], "the watcher's move to reach the host")
|
||||
firefox.switch_to(guest_tab)
|
||||
firefox.run("document.getElementById('undo').click();")
|
||||
self.wait_until(lambda s: s["hides"] == made["hides"], "the watcher's Undo")
|
||||
firefox.switch_to(host_tab)
|
||||
self.wait_until(lambda s: s["hides"] == made["hides"], "the Undo to reach the host")
|
||||
firefox.switch_to(guest_tab)
|
||||
|
||||
# The host moves it by a hold, resizes it freely from a corner,
|
||||
# and the file carries it above every layer.
|
||||
@@ -1409,43 +1435,36 @@ class EndToEndTest(unittest.TestCase):
|
||||
firefox.run("document.querySelector('[data-tool=pen]').click();")
|
||||
scaled = erased_not
|
||||
|
||||
# A guest given the right adds an image, keeps the power to remove it
|
||||
# after the right is withdrawn, and cannot remove the host's.
|
||||
self.assertEqual(firefox.run(TOGGLE_PERMISSION, "Viewer"), "true")
|
||||
# The guest adds an image and removes it with a tap of the eraser,
|
||||
# nobody's leave being needed (2026-10-18).
|
||||
firefox.switch_to(guest_tab)
|
||||
self.wait_until(lambda s: not firefox.run("return document.getElementById('new_page').hidden;"),
|
||||
"the viewer to be allowed")
|
||||
self.assertFalse(firefox.run("return document.getElementById('new_page').hidden;"))
|
||||
firefox.run_until_done(LOAD_IMAGE, 200, 100, "png", 0, False)
|
||||
added = self.wait_until(lambda s: len(s["images"]) == 3, "the viewer's image")
|
||||
firefox.switch_to(host_tab)
|
||||
self.wait_until(lambda s: len(s["images"]) == 3, "the viewer's image to reach the host")
|
||||
self.assertEqual(firefox.run(TOGGLE_PERMISSION, "Viewer"), "false")
|
||||
firefox.switch_to(guest_tab)
|
||||
self.wait_until(lambda s: firefox.run("return document.getElementById('new_page').hidden;"),
|
||||
"the right to be withdrawn")
|
||||
own = added["images"][2]
|
||||
firefox.run(ERASER_AT, own["x"] + 100, own["y"] + 50, 5)
|
||||
self.wait_until(lambda s: len(s["images"]) == 2, "the viewer's own image removed")
|
||||
hosts = self.state()["images"][1]
|
||||
firefox.run(ERASER_AT, hosts["x"] + 50, hosts["y"] + 50, 5)
|
||||
time.sleep(0.5)
|
||||
self.assertEqual(len(self.state()["images"]), 2)
|
||||
firefox.switch_to(host_tab)
|
||||
self.wait_until(lambda s: len(s["images"]) == 2, "the removal to reach the host")
|
||||
firefox.switch_to(guest_tab)
|
||||
|
||||
# The viewer sees the same, and cannot move an image: a hold there
|
||||
# draws a dot instead.
|
||||
firefox.switch_to(guest_tab)
|
||||
# The viewer sees the same, and moves the host's image by a hold,
|
||||
# which draws no dot; the host sees the move.
|
||||
viewer = self.wait_until(lambda s: s["images"] and rect(s, 1) == rect(scaled, 1), "the viewer's copy")
|
||||
self.assertEqual(viewer["images"][1]["hash"], scaled["images"][1]["hash"])
|
||||
vscale = firefox.run("return window.kokkoja.renderer.cssPixelsPerUnit;")
|
||||
viewer_strokes = len(viewer["layers"].get("Viewer", []))
|
||||
firefox.run_until_done(PEN_HOLD, (viewer["images"][1]["x"] + 50 - viewer["origin"][0]) * vscale,
|
||||
(viewer["images"][1]["y"] + 50 - viewer["origin"][1]) * vscale, 60 * vscale, 0, 800)
|
||||
time.sleep(0.5)
|
||||
after = self.state()
|
||||
self.assertEqual(rect(after, 1), rect(scaled, 1))
|
||||
self.assertEqual(len(after["layers"].get("Viewer", [])), 1)
|
||||
after = self.wait_until(lambda s: s["images"][1]["x"] == scaled["images"][1]["x"] + 60, "the image moved by the viewer")
|
||||
self.assertEqual(rect(after, 1)[1:], rect(scaled, 1)[1:])
|
||||
self.assertEqual(len(after["layers"].get("Viewer", [])), viewer_strokes)
|
||||
firefox.switch_to(host_tab)
|
||||
self.wait_until(lambda s: s["images"][1]["x"] == scaled["images"][1]["x"] + 60, "the viewer's move to reach the host")
|
||||
firefox.switch_to(guest_tab)
|
||||
|
||||
firefox.in_session("DELETE", "/window")
|
||||
firefox.switch_to(host_tab)
|
||||
@@ -1501,8 +1520,9 @@ class EndToEndTest(unittest.TestCase):
|
||||
image (and, until 2026-10-14, every Hide and own text rectangle,
|
||||
which carry blue corners at all times since), a drag inside one
|
||||
moves it and a drag on a corner resizes it, at once, no hold; a
|
||||
press elsewhere does nothing. A guest without the right sees no
|
||||
cues, their own text rectangle's corners being drawn anyway."""
|
||||
press elsewhere does nothing. A guest sees the same cues on the
|
||||
image and the Hide (2026-10-18; none before), and one on their
|
||||
own text."""
|
||||
firefox = self.firefox
|
||||
host_tab = firefox.current_tab()
|
||||
firefox.go(self.address("Late"))
|
||||
@@ -1640,17 +1660,17 @@ class EndToEndTest(unittest.TestCase):
|
||||
firefox.pointer_actions([{"type": "pointerUp", "button": 0}])
|
||||
self.assertEqual(cues(), ["image", "hide", "text", "text"])
|
||||
|
||||
# The watcher, without the right, is shown no cue on the image or
|
||||
# the Hide, only on their own text.
|
||||
# The watcher is shown the cues on the image and the Hide too, and
|
||||
# on their own text.
|
||||
firefox.switch_to(guest_tab)
|
||||
self.wait_until(lambda s: len(s["images"]) == 1 and len(s["hides"]) == 1, "the page at the watcher")
|
||||
choose("arrange")
|
||||
self.assertEqual(cues(), [])
|
||||
self.assertEqual(cues(), ["image", "hide"])
|
||||
choose("text")
|
||||
firefox.run(TEXT_GESTURE, 0.1, 0.8, 0.3, 0.9)
|
||||
firefox.type_into(".text_field", "Mine")
|
||||
choose("arrange")
|
||||
self.assertEqual(cues(), ["text"])
|
||||
self.assertEqual(cues(), ["image", "hide", "text"])
|
||||
mine = self.wait_until(lambda s: s["texts"].get("Watcher") and s["texts"]["Watcher"][0]["text"] == "Mine", "the watcher's box")
|
||||
mine = mine["texts"]["Watcher"][0]
|
||||
self.assert_hover_cue(mine["x"] + 20, mine["y"] + 20, [mine["x"], mine["y"], mine["width"], mine["height"]], "rect")
|
||||
@@ -2224,7 +2244,7 @@ class EndToEndTest(unittest.TestCase):
|
||||
self.assertNotIn("Copy", names)
|
||||
self.assertEqual(len(names), len(set(names)), "no two entries of one name")
|
||||
self.assertIn("\nPen\nWrite on the page. Only your own writing can be erased.\n", manual)
|
||||
self.assertIn("\nFit to the window\nShow the whole page in the window.", manual)
|
||||
self.assertIn("\nFit to the window\nShow the whole page in the window, on your screen only.", manual)
|
||||
self.assertTrue(all(len(line) <= 80 for line in manual.split("\n")), "wrapped at 80")
|
||||
self.assertTrue(any(len(line) > 60 for line in manual.split("\n")), "filled, not one word a line")
|
||||
# Every tooltip is in it; those that differ only by a number (the
|
||||
@@ -2460,7 +2480,6 @@ class EndToEndTest(unittest.TestCase):
|
||||
"the student to receive the teacher's layer")
|
||||
self.assertEqual(student["layers"]["Teacher"], teacher["layers"]["Teacher"])
|
||||
teacher_first_page = {"Teacher": teacher["layers"]["Teacher"]}
|
||||
self.assertEqual([p["host"] for p in student["participants"]], [False, True])
|
||||
|
||||
# The student writes with a pen; the teacher sees the same strokes.
|
||||
for fx in (0.15, 0.4, 0.65):
|
||||
@@ -2545,12 +2564,11 @@ class EndToEndTest(unittest.TestCase):
|
||||
self.assertIn("Already kept", firefox.run(
|
||||
"return document.getElementById('notice').textContent;"))
|
||||
|
||||
# Only the host can start a new page.
|
||||
self.assertTrue(firefox.run("return document.getElementById('new_page').hidden;"))
|
||||
firefox.switch_to(teacher_tab)
|
||||
# Everyone has New (2026-10-18; the host alone until then).
|
||||
self.assertFalse(firefox.run("return document.getElementById('new_page').hidden;"))
|
||||
firefox.switch_to(teacher_tab)
|
||||
|
||||
# The host starts a new page. Everyone moves to it, and everyone's
|
||||
# The teacher starts a new page. Everyone moves to it, and everyone's
|
||||
# old page is kept without anyone asking: the teacher's now, the
|
||||
# student's already.
|
||||
firefox.run("document.getElementById('new_page').click();")
|
||||
@@ -2624,8 +2642,10 @@ class EndToEndTest(unittest.TestCase):
|
||||
self.assertEqual(doubled["strokes"], before["strokes"])
|
||||
firefox.run(CTRL_DRAG, 0.5, 0.3, 0.5, 0.3, 104)
|
||||
self.assertAlmostEqual(zoom()["width"], before["width"], delta=2)
|
||||
# The host's Fit and Normal scale reach the guest, who then fits the page to
|
||||
# the guest's own window; the guest's own Fit stays the guest's.
|
||||
# Fit and Normal scale are each person's own (2026-10-18; the
|
||||
# host's reached everyone from 2026-10-08 until then): the
|
||||
# teacher's Fit leaves the student's zoom alone, and the student's
|
||||
# Normal scale the teacher's.
|
||||
firefox.switch_to(student_tab)
|
||||
firefox.run("""
|
||||
document.getElementById('desk').dispatchEvent(new WheelEvent('wheel',
|
||||
@@ -2634,12 +2654,13 @@ class EndToEndTest(unittest.TestCase):
|
||||
guest_zoomed = firefox.run("return window.kokkoja.renderer.cssPixelsPerUnit;")
|
||||
firefox.switch_to(teacher_tab)
|
||||
firefox.run("document.getElementById('zoom_fit').click();")
|
||||
firefox.switch_to(student_tab)
|
||||
self.wait_until_true(lambda: firefox.run("return window.kokkoja.renderer.cssPixelsPerUnit;"),
|
||||
lambda v: v < guest_zoomed, "the guest's view to follow the host's Fit")
|
||||
firefox.run("document.getElementById('zoom_sheet').click();")
|
||||
firefox.switch_to(teacher_tab)
|
||||
host_scale = firefox.run("return window.kokkoja.renderer.cssPixelsPerUnit;")
|
||||
firefox.switch_to(student_tab)
|
||||
time.sleep(0.5)
|
||||
self.assertEqual(firefox.run("return window.kokkoja.renderer.cssPixelsPerUnit;"), guest_zoomed)
|
||||
firefox.run("document.getElementById('zoom_sheet').click();")
|
||||
self.assertNotEqual(firefox.run("return window.kokkoja.renderer.cssPixelsPerUnit;"), guest_zoomed)
|
||||
firefox.switch_to(teacher_tab)
|
||||
time.sleep(0.5)
|
||||
self.assertEqual(firefox.run("return window.kokkoja.renderer.cssPixelsPerUnit;"), host_scale)
|
||||
|
||||
@@ -2679,7 +2700,7 @@ class EndToEndTest(unittest.TestCase):
|
||||
self.assertGreater(wide_page["height"], teacher["page"]["height"])
|
||||
firefox.switch_to(student_tab)
|
||||
self.wait_until(lambda s: s["page"] == wide_page, "the expanded page to reach the student")
|
||||
self.assertTrue(firefox.run("return document.getElementById('zoom_expand').hidden;"))
|
||||
self.assertFalse(firefox.run("return document.getElementById('zoom_expand').hidden;"))
|
||||
self.assertEqual(firefox.run("return window.kokkoja.session.page.baseLeft;"),
|
||||
-((wide_page["width"] - teacher["page"]["width"]) // 2))
|
||||
# The page file carries the sheet's edges and reads back with them.
|
||||
@@ -3007,8 +3028,8 @@ class EndToEndTest(unittest.TestCase):
|
||||
self.assertEqual(alone["page"], teacher["page"])
|
||||
self.assertEqual(alone["image"], teacher["image"])
|
||||
self.assertEqual(alone["participants"], [
|
||||
{"name": "Teacher", "host": True, "present": True},
|
||||
{"name": "Student", "host": False, "present": False}])
|
||||
{"name": "Teacher", "present": True},
|
||||
{"name": "Student", "present": False}])
|
||||
firefox.run("document.getElementById('zoom_fit').click();") # The view came back scrolled.
|
||||
firefox.drag("#page", 100, 500, 300, 520)
|
||||
self.current_page("Teacher notebook", {"Teacher": 1, "Student": 1}, number=3)
|
||||
@@ -3082,22 +3103,14 @@ class EndToEndTest(unittest.TestCase):
|
||||
return [shown.naturalWidth, shown.naturalHeight];
|
||||
"""), [2400, 1600])
|
||||
|
||||
# The student joins the new session. The host lets the student
|
||||
# start pages, the student loads an image, and the host withdraws
|
||||
# the permission.
|
||||
# The student joins the new session and starts a page with an
|
||||
# image on it, as anyone may (2026-10-18).
|
||||
invitation = firefox.run("return document.getElementById('invitation').value;")
|
||||
firefox.switch_to(student_tab)
|
||||
firefox.go(self.address("Student", join=invitation))
|
||||
self.enter_name("Student", "join_session")
|
||||
self.wait_until(lambda s: s["page"]["number"] == 2 and s["image"] and s["image"]["ready"],
|
||||
"the student to reach the large image page")
|
||||
self.assertTrue(firefox.run("return document.getElementById('new_page').hidden;"))
|
||||
firefox.switch_to(teacher_tab)
|
||||
self.assertEqual(firefox.run(TOGGLE_PERMISSION, "Student"), "true")
|
||||
firefox.switch_to(student_tab)
|
||||
self.wait_until(
|
||||
lambda s: not firefox.run("return document.getElementById('new_page').hidden;"),
|
||||
"the student to be allowed to start pages")
|
||||
firefox.run_until_done(LOAD_IMAGE, 300, 400, "png", 0, True)
|
||||
student = self.wait_until(lambda s: s["page"]["number"] == 3, "the student's image page")
|
||||
self.assertEqual(student["page"], {"number": 3, "width": 2970, "height": 2100})
|
||||
@@ -3106,14 +3119,7 @@ class EndToEndTest(unittest.TestCase):
|
||||
lambda s: s["page"]["number"] == 3 and s["image"] and s["image"]["ready"],
|
||||
"the student's image to reach the teacher")
|
||||
self.assertEqual(teacher["image"], student["image"])
|
||||
self.assertEqual(firefox.run(TOGGLE_PERMISSION, "Student"), "false")
|
||||
firefox.switch_to(student_tab)
|
||||
self.wait_until(
|
||||
lambda s: firefox.run("return document.getElementById('new_page').hidden;"),
|
||||
"the student's permission to be withdrawn")
|
||||
firefox.run("window.kokkoja.session.newPage();")
|
||||
time.sleep(1)
|
||||
self.assertEqual(self.state()["page"]["number"], 3)
|
||||
|
||||
# Writing past the edge of the page makes the page grow, for
|
||||
# everyone, in steps of 5 mm.
|
||||
@@ -3370,19 +3376,6 @@ class EndToEndTest(unittest.TestCase):
|
||||
"""), lambda names: "Other notebook" not in names, "the toolbar's list to lose the notebook")
|
||||
firefox.run("document.getElementById('chooser_close').click();")
|
||||
|
||||
# A guest has the chooser too, to look at and clear their own
|
||||
# notebook; a card tells them that loading is the host's.
|
||||
firefox.switch_to(student_tab)
|
||||
number = self.state()["page"]["number"]
|
||||
firefox.run("document.getElementById('load_notebook_page').click();")
|
||||
self.wait_until_true(lambda: firefox.run("""
|
||||
return document.querySelectorAll('#chooser_pages .page_card').length;
|
||||
"""), lambda n: n >= 1, "the student's chooser to list kept pages")
|
||||
firefox.run("document.querySelector('#chooser_pages .page_card').click();")
|
||||
self.assertIn("Only the host and co-hosts", firefox.run("return document.getElementById('chooser_message').textContent;"))
|
||||
self.assertTrue(firefox.run("return document.getElementById('notebook_chooser').open;"))
|
||||
self.assertEqual(self.state()["page"]["number"], number)
|
||||
firefox.run("document.getElementById('chooser_close').click();")
|
||||
firefox.switch_to(teacher_tab)
|
||||
# A page that its images have made longer than a sheet is first
|
||||
# shown at the scale at which one sheet fits the window.
|
||||
@@ -3394,7 +3387,20 @@ class EndToEndTest(unittest.TestCase):
|
||||
"the loaded page of three images to reach the student")
|
||||
self.assertEqual(student["images"], stacked["images"])
|
||||
self.assertEqual(firefox.run(VIEW), long_view)
|
||||
|
||||
# The student's chooser loads a kept page for everyone too
|
||||
# (2026-10-18; a card refused until then): a new page, the
|
||||
# teacher following.
|
||||
number = student["page"]["number"]
|
||||
firefox.run("document.getElementById('load_notebook_page').click();")
|
||||
self.wait_until_true(lambda: firefox.run("""
|
||||
return document.querySelectorAll('#chooser_pages .page_card').length;
|
||||
"""), lambda n: n >= 1, "the student's chooser to list kept pages")
|
||||
firefox.run("document.querySelector('#chooser_pages .page_card').click();")
|
||||
self.wait_until(lambda s: s["page"]["number"] == number + 1, "the page the student loaded")
|
||||
self.assertFalse(firefox.run("return document.getElementById('notebook_chooser').open;"))
|
||||
firefox.switch_to(teacher_tab)
|
||||
self.wait_until(lambda s: s["page"]["number"] == number + 1, "the student's loaded page to reach the teacher")
|
||||
|
||||
# Five colours and four widths in the toolbar. "+" among the
|
||||
# colours opens a palette of our own, in which the colours already
|
||||
@@ -3557,9 +3563,10 @@ class EndToEndTest(unittest.TestCase):
|
||||
self.assertTrue(all(abs(a - b) <= 3 for a, b in zip(colours["image"], (200, 60, 60))), colours)
|
||||
firefox.run("document.querySelector('#participants input[type=checkbox]').click();")
|
||||
|
||||
# A tap of the eraser on an image removes it, for whoever may make
|
||||
# pages or added the image; a sweep across it does not. Writing on
|
||||
# it stays; the sheet keeps its size.
|
||||
# A tap of the eraser on an image removes it, by anyone (2026-10-18;
|
||||
# the host, a co-host or whoever added it until then); a sweep
|
||||
# across it does not. Writing on it stays; the sheet keeps its
|
||||
# size.
|
||||
firefox.run("document.getElementById('new_page').click();")
|
||||
number = self.wait_until(lambda s: not s["images"], "another new page")["page"]["number"]
|
||||
firefox.run_until_done(LOAD_IMAGE, 300, 400, "png", 0, False)
|
||||
@@ -3571,18 +3578,19 @@ class EndToEndTest(unittest.TestCase):
|
||||
firefox.switch_to(student_tab)
|
||||
self.wait_until(lambda s: len(s["images"]) == 2 and all(i["ready"] for i in s["images"]),
|
||||
"the two images to reach the student")
|
||||
# Not the student, who has no right and did not add it.
|
||||
firefox.run(ERASER_AT, 1050, 1500, 5)
|
||||
# Not by moving the eraser across it: the student's sweep leaves it.
|
||||
firefox.run(ERASER_AT, 1050, 1500, 100)
|
||||
firefox.run(PEN_WAVE, 0.3, 0.5)
|
||||
firefox.switch_to(teacher_tab)
|
||||
self.wait_until(lambda s: len(s["layers"].get("Student", [])) == 1 and s["layers"]["Student"][0]["done"],
|
||||
"the student's writing on the image")
|
||||
# Not by moving the eraser across it.
|
||||
firefox.run(ERASER_AT, 1050, 1500, 100)
|
||||
time.sleep(0.3)
|
||||
self.assertEqual(rectangles(self.state()), rectangles(two))
|
||||
# A tap by the teacher removes it, written on or not.
|
||||
# A tap by the student, who did not add it, removes it, written on
|
||||
# or not; the teacher sees it go.
|
||||
firefox.switch_to(student_tab)
|
||||
firefox.run(ERASER_AT, 1050, 1500, 5)
|
||||
self.wait_until(lambda s: len(s["images"]) == 1, "the image removed by the student")
|
||||
firefox.switch_to(teacher_tab)
|
||||
one = self.wait_until(lambda s: len(s["images"]) == 1, "the image to be removed")
|
||||
self.assertEqual(one["page"], {"number": number, "width": 2970, "height": 3250})
|
||||
self.assertEqual(rectangles(one), [[1335, 50, 300, 400]])
|
||||
|
||||
Reference in new issue
Block a user