fix(r3): fold heid's code review — equal stage widths, the axis guard, players, and tests that read the observable
The one drift: the separator was a border on B, making B's stage 1px narrower than A's; it is now a 1px column gap, so the stages are the same size to the pixel. Tests now read what the contract promises instead of a proxy: the strip's ring order, the full bakeoff sequence, 1:1 and Fit by geometry, the 900px break from both sides, A wrapping, each form naming its own item, a sibling-prefix symlink, both reveals, the strip's flag, the back arrow unlinked, a one-axis picture, a focused player, two videos with no toggle. The contract names .cmp-cap, a press on a stage, INV-4's URL-driven picker and the redirect branch's isinstance check. r3.toml gains ten rows.
This commit is contained in:
@@ -39,15 +39,17 @@ _BOXES = """() => [...document.querySelectorAll('.cmp-side .vstage')].map(s => {
|
||||
|
||||
|
||||
def test_two_stages_side_by_side_wide_and_stacked_narrow(browser, live):
|
||||
"""The tracer. At 1440 A and B share one row, A on the left, each about
|
||||
half the body. At 390 they stack, A above B, each at most 45vh tall."""
|
||||
"""The tracer. Above 900px A and B share one row, A on the left, each
|
||||
exactly half the body — the SAME width, so equal pictures have equal
|
||||
ranges. At 900px and below (the review's break) they stack, A above B,
|
||||
each at most 45vh tall."""
|
||||
base, root = live
|
||||
from booth.app import set_blurred
|
||||
b = _pics(root, {"a.png": (800, 600), "b.png": (800, 600)})
|
||||
(b / "a.png.txt").write_text("a caption on A only, " * 8)
|
||||
set_blurred(b, "b.png", True) # the bar carries Reveal all too: its fullest
|
||||
got = {}
|
||||
for w, h in ((1440, 900), (390, 844)):
|
||||
for w, h in ((1440, 900), (901, 800), (900, 800), (390, 844)):
|
||||
page = browser.new_page(viewport={"width": w, "height": h})
|
||||
_open(page, f"{base}/b/g/compare?a=a.png&b=b.png")
|
||||
got[w] = (page.evaluate(_BOXES), h,
|
||||
@@ -67,7 +69,13 @@ def test_two_stages_side_by_side_wide_and_stacked_narrow(browser, live):
|
||||
# the SAME stage for both, or Fit draws one smaller: A's caption must not
|
||||
# take its height from A's stage alone
|
||||
assert abs(a["h"] - b["h"]) <= 1 and abs(a["b"] - b["b"]) <= 1, (a, b)
|
||||
# ...and the same width: a separator must not come out of one side alone
|
||||
assert abs((a["r"] - a["l"]) - (b["r"] - b["l"])) <= 0.5, (a, b)
|
||||
assert over <= 0 and tallest[0] <= 40 and tallest[1] <= 1, (over, tallest)
|
||||
(a, b), _, _, _ = got[901]
|
||||
assert abs(a["t"] - b["t"]) <= 1 and a["r"] <= b["l"], ("side by side at 901", a, b)
|
||||
(a, b), _, _, _ = got[900]
|
||||
assert a["b"] <= b["t"], ("stacked at 900", a, b)
|
||||
(a, b), vh, over, tallest = got[390]
|
||||
assert a["b"] <= b["t"], ("A above B", a, b)
|
||||
assert a["h"] <= 0.45 * vh + 1 and b["h"] <= 0.45 * vh + 1, (a, b)
|
||||
@@ -101,7 +109,10 @@ def test_one_mode_for_both_and_for_the_review(browser, live):
|
||||
stored = page.evaluate("localStorage.getItem('booth.fit')")
|
||||
pressed = page.locator("#btn-one").get_attribute("aria-pressed")
|
||||
page.goto(f"{base}/b/g/view?f=a.png", wait_until="networkidle")
|
||||
review_one = page.evaluate("document.documentElement.classList.contains('stage-one')")
|
||||
page.wait_for_function("document.getElementById('vimg').complete && document.getElementById('vimg').naturalWidth > 0")
|
||||
review_one = page.evaluate("""() => { const i = document.getElementById('vimg'), b = i.getBoundingClientRect();
|
||||
return document.documentElement.classList.contains('stage-one') &&
|
||||
Math.round(b.width) === i.naturalWidth && Math.round(b.height) === i.naturalHeight; }""")
|
||||
page.goto(f"{base}/b/g/compare?a=a.png&b=b.png", wait_until="networkidle")
|
||||
page.keyboard.press("Z")
|
||||
back = page.evaluate("[document.documentElement.classList.contains('stage-one'), localStorage.getItem('booth.fit')]")
|
||||
@@ -202,6 +213,28 @@ def test_synced_pan_by_fraction_for_different_sizes(browser, live):
|
||||
assert quarter[1] == [x, y] and idle == quarter, (x, y, quarter, idle)
|
||||
|
||||
|
||||
def test_an_axis_with_nothing_to_scroll_is_ignored(browser, live):
|
||||
"""A side with nothing to scroll on an axis ignores that axis, each axis
|
||||
on its own. A is wide and short (it scrolls across only), B is large. B
|
||||
scrolled down stays down when A pans across: B's x follows, B's y is B's."""
|
||||
base, root = live
|
||||
_pics(root, {"a.png": (3000, 200), "b.png": (3000, 3000)})
|
||||
page = browser.new_page(viewport={"width": 1440, "height": 900})
|
||||
_one_to_one(page, f"{base}/b/g/compare?a=a.png&b=b.png")
|
||||
(rax, ray), (rbx, rby) = page.evaluate(_RANGES)
|
||||
page.evaluate("document.querySelector('.cmp-side[data-side=\"b\"] .vstage').scrollTo(0, 500)")
|
||||
page.wait_for_timeout(250)
|
||||
down = page.evaluate(_SCROLLS)
|
||||
page.evaluate("document.querySelector('.cmp-side[data-side=\"a\"] .vstage').scrollTo(800, 0)")
|
||||
page.wait_for_timeout(250)
|
||||
across = page.evaluate(_SCROLLS)
|
||||
page.close()
|
||||
assert ray == 0 and rax > 0 and rby > 0, (rax, ray, rbx, rby)
|
||||
assert down == [[0, 0], [0, 500]], down
|
||||
assert across[0] == [800, 0], across
|
||||
assert abs(across[1][0] - 800 / rax * rbx) <= 1 and across[1][1] == 500, across
|
||||
|
||||
|
||||
def test_a_flags_A_in_place_and_the_stages_survive(browser, live):
|
||||
"""`A` flags A in place: no navigation, A's control and label show the
|
||||
flag, B's do not, and both stages are the SAME nodes — a save swaps the
|
||||
@@ -221,6 +254,7 @@ def test_a_flags_A_in_place_and_the_stages_survive(browser, live):
|
||||
a: document.querySelector('[data-region="label-a"]').textContent,
|
||||
b: document.querySelector('[data-region="label-b"]').textContent,
|
||||
bflag: document.getElementById('cmp-flag-b').classList.contains('is-flagged'),
|
||||
strip: [...document.querySelectorAll('.film-f.is-flagged')].map(f => f.dataset.rel),
|
||||
})""")
|
||||
flagged = [m.target for m in marks_for(b) if m.shape == "flag"]
|
||||
# the save REPLACED the buttons: each key must find the fresh one
|
||||
@@ -233,6 +267,7 @@ def test_a_flags_A_in_place_and_the_stages_survive(browser, live):
|
||||
page.close()
|
||||
assert not got["reload"] and got["same"], got
|
||||
assert "flagged" in got["a"] and "flagged" not in got["b"] and not got["bflag"], got
|
||||
assert got["strip"] == ["a.png"], got
|
||||
assert flagged == ["a.png"] and after == ["b.png"] and not reloaded, (flagged, after, reloaded)
|
||||
|
||||
|
||||
@@ -267,9 +302,9 @@ def test_linked_arrow_walks_a_bakeoff(browser, live):
|
||||
_press_and_wait(page, "ArrowRight")
|
||||
pairs.append(_pair(page))
|
||||
page.close()
|
||||
assert [(a[2:], b[2:]) for a, b in pairs] == [(a[2:], a[2:]) for a, _ in pairs], pairs
|
||||
assert [a[0] + b[0] for a, b in pairs] == ["mr"] * 4, pairs
|
||||
assert pairs[-1] == ("m-4-tower-s42.png", "r-4-tower-s42.png"), pairs
|
||||
# every pair, in order: no pair skipped and none repeated
|
||||
scenes = ("dock-s11", "forge-s23", "marsh-s37", "tower-s42")
|
||||
assert pairs == [(f"m-{i}-{sc}.png", f"r-{i}-{sc}.png") for i, sc in enumerate(scenes, 1)], pairs
|
||||
|
||||
|
||||
def test_unlinked_moves_only_the_active_side_and_the_strip_picks_it(browser, live):
|
||||
@@ -286,6 +321,9 @@ def test_unlinked_moves_only_the_active_side_and_the_strip_picks_it(browser, liv
|
||||
one = _pair(page)
|
||||
_press_and_wait(page, "ArrowRight")
|
||||
two = _pair(page)
|
||||
_press_and_wait(page, "ArrowLeft")
|
||||
left = _pair(page)
|
||||
_press_and_wait(page, "ArrowRight")
|
||||
with page.expect_navigation(wait_until="networkidle"):
|
||||
page.locator('.film-f[data-rel="m-3-marsh-s37.png"]').click()
|
||||
picked = _pair(page)
|
||||
@@ -301,6 +339,7 @@ def test_unlinked_moves_only_the_active_side_and_the_strip_picks_it(browser, liv
|
||||
assert linked == "false", linked
|
||||
assert one == ("m-1-dock-s11.png", "r-2-forge-s23.png"), one
|
||||
assert two == ("m-1-dock-s11.png", "r-3-marsh-s37.png"), two
|
||||
assert left == one, ("← moves only the active side back", left)
|
||||
assert picked == ("m-1-dock-s11.png", "m-3-marsh-s37.png"), picked
|
||||
assert active == ["a", ["m-1-dock-s11.png"]], active
|
||||
assert stepped == ("m-2-forge-s23.png", "m-3-marsh-s37.png"), stepped
|
||||
@@ -328,6 +367,10 @@ def test_blur_is_honest_on_both_sides(browser, live):
|
||||
page.wait_for_timeout(400) # the filter transition
|
||||
own = page.evaluate(_FILTERS)
|
||||
page.locator('.cmp-reveal[data-side="b"]').click()
|
||||
page.locator('.cmp-reveal[data-side="a"]').click()
|
||||
page.wait_for_timeout(400)
|
||||
own_a = page.evaluate(_FILTERS)
|
||||
page.locator('.cmp-reveal[data-side="a"]').click()
|
||||
page.locator("[data-reveal-all]").click()
|
||||
page.wait_for_timeout(400)
|
||||
everything = page.evaluate(_FILTERS)
|
||||
@@ -336,6 +379,7 @@ def test_blur_is_honest_on_both_sides(browser, live):
|
||||
assert all("blur(" in f for f in before), before
|
||||
assert shown == [True, True], shown
|
||||
assert "blur(" in own[0] and "blur(" not in own[1], own
|
||||
assert "blur(" not in own_a[0] and "blur(" in own_a[1], own_a
|
||||
assert all("blur(" not in f for f in everything), everything
|
||||
assert stood_down == [False, False], stood_down
|
||||
|
||||
@@ -385,7 +429,9 @@ def test_without_js_every_judgment_and_step_still_works(browser, live):
|
||||
ctx = browser.new_context(java_script_enabled=False, viewport={"width": 1440, "height": 900})
|
||||
page = ctx.new_page()
|
||||
page.goto(f"{base}/b/g/compare?a=m-1-dock-s11.png&b=r-1-dock-s11.png", wait_until="networkidle")
|
||||
fit = page.evaluate("document.documentElement.classList.contains('stage-one')")
|
||||
fit = page.evaluate("""() => document.documentElement.classList.contains('stage-one') ||
|
||||
[...document.querySelectorAll('.cmp-side .vstage img')].some(i =>
|
||||
Math.round(i.getBoundingClientRect().width) === i.naturalWidth)""")
|
||||
hidden = [page.locator(s).is_visible() for s in ("#cmp-link", "#vtoggle")]
|
||||
forms = page.locator(".cmp-flag form").count()
|
||||
page.locator('a[data-step="both-next"]').click()
|
||||
@@ -417,6 +463,8 @@ def test_the_keys_keep_the_reviews_guards_and_c_toggles_the_view(browser, live):
|
||||
item; `Esc` (or `C`) in compare returns to the review of A."""
|
||||
base, root = live
|
||||
_bakeoff(root)
|
||||
v = _pics(root, {"a.png": (400, 300)}, name="v")
|
||||
(v / "b.webm").write_bytes(b"\x1aE\xdf\xa3")
|
||||
page = browser.new_page(viewport={"width": 1440, "height": 900})
|
||||
_open(page, f"{base}/b/g/compare?a=m-2-forge-s23.png&b=r-2-forge-s23.png")
|
||||
start = page.url
|
||||
@@ -432,6 +480,13 @@ def test_the_keys_keep_the_reviews_guards_and_c_toggles_the_view(browser, live):
|
||||
pressed = (page.url.split("?")[1], page.locator("#cmp-link").get_attribute("aria-pressed"))
|
||||
page.locator("#cmp-link").press(" ") # linked again
|
||||
page.evaluate("document.activeElement.blur()")
|
||||
# ...and never from a player on either stage: Space is the player's
|
||||
page.goto(f"{base}/b/v/compare?a=a.png&b=b.webm", wait_until="networkidle")
|
||||
page.locator(".cmp-media").focus()
|
||||
page.keyboard.press(" ")
|
||||
page.wait_for_timeout(300)
|
||||
player = page.url.endswith("/b/v/compare?a=a.png&b=b.webm")
|
||||
page.goto(start, wait_until="networkidle")
|
||||
_press_and_wait(page, " ")
|
||||
fwd = _pair(page)
|
||||
_press_and_wait(page, "Shift+ ")
|
||||
@@ -444,6 +499,7 @@ def test_the_keys_keep_the_reviews_guards_and_c_toggles_the_view(browser, live):
|
||||
c_back = page.url
|
||||
page.close()
|
||||
assert inert == (True, "b", "true"), inert
|
||||
assert player, "Space on a focused player stepped the pair"
|
||||
assert pressed == ("a=m-2-forge-s23.png&b=r-2-forge-s23.png&link=0", "false"), pressed
|
||||
assert fwd == ("m-3-marsh-s37.png", "r-3-marsh-s37.png") and back == ("m-2-forge-s23.png", "r-2-forge-s23.png"), (fwd, back)
|
||||
assert esc.endswith("/b/g/view?f=m-2-forge-s23.png"), esc
|
||||
|
||||
Reference in New Issue
Block a user