feat(blur): reveal all, and the booth blur control (r2b merge 1: D2 + D2b)
The operator ruled blur A, and made it urgent: "per booth blurring is now
important since we are showing up to 4 images."
- Reveal all: one control per booth, in the booth header and the review's
top bar, outside every data-region. It is in the markup only when
something is blurred, always `hidden` until the script shows it.
- The state is sessionStorage per booth, per tab, and nothing reaches
the server. It is carried as one `reveal-all` class on <html>, applied
before first paint from the page's own data-booth, so booth A's reveal
cannot follow you into booth B and the index is never revealed.
- Per-item reveal buttons stand down by stylesheet, and an item's own
reveal is never touched, so "blur again" restores each item as it was.
- A storage write that throws still applies the click.
- The booth blur control: a plain form to booth-dev's POST /blurbooth, so
it works with scripts off. Its label follows is_booth_blurred; from the
review it carries `back` and lands on the same item. A fogged booth's
Desk row says "◉ blurred".
- Found by rendering it: under a fogged booth every item reported
`blurred`, so an item blurred only by the booth offered an un-blur that
visibly did nothing. The gallery now carries `blurred_self`, and such an
item shows "◉ booth", a label rather than a control.
Contract docs/contracts/r2b_desk_reveal_theme.contract.md (heid contract
panel 4/4, folded). tests/mutations/r2b.toml: 14/14 proved. 765 passed.
This commit is contained in:
@@ -550,3 +550,137 @@ def test_the_wipe_dialog_shows_what_is_being_wiped_and_never_fails_open(browser,
|
||||
shown = said[0].split("\n\n")[0]
|
||||
assert not any(c in shown for c in "\n"), repr(shown)
|
||||
assert "safe�gnp.xe�line2" in shown, repr(shown)
|
||||
|
||||
|
||||
_FILTER = "sel => { const e = document.querySelector(sel); return e ? getComputedStyle(e).filter : 'MISSING'; }"
|
||||
|
||||
|
||||
def _blurred_set(root: pathlib.Path, name: str = "g", blur=("a.png",), flag=("a.png",)) -> pathlib.Path:
|
||||
from booth.app import set_blurred
|
||||
from booth.marks import set_flag
|
||||
b = root / name
|
||||
b.mkdir()
|
||||
for rel in ("a.png", "b.png", "c.png"):
|
||||
(b / rel).write_bytes(PNG)
|
||||
for rel in blur:
|
||||
set_blurred(b, rel, True)
|
||||
for rel in flag:
|
||||
set_flag(b, rel, True)
|
||||
return b
|
||||
|
||||
|
||||
def _settle(page) -> None:
|
||||
page.wait_for_timeout(400) # the filter transition (--dur-2)
|
||||
|
||||
|
||||
def test_reveal_all_reveals_every_blurred_surface_and_survives_the_next_page(browser, live):
|
||||
"""r2b D2 (blur ruling A): one click lifts the blur on the tile, the tray,
|
||||
the review stage and the filmstrip, and it holds on the next page of the
|
||||
same tab. A fresh tab is blurred again — per tab, never persisted."""
|
||||
base, root = live
|
||||
_blurred_set(root)
|
||||
ctx = browser.new_context(viewport={"width": 1400, "height": 900})
|
||||
page = ctx.new_page()
|
||||
page.goto(f"{base}/b/g/", wait_until="networkidle")
|
||||
tile, tray = 'figure.item[data-item="a.png"] img', ".tray-item.is-blurred img"
|
||||
before = [page.evaluate(_FILTER, tile), page.evaluate(_FILTER, tray)]
|
||||
page.locator("[data-reveal-all]").click()
|
||||
_settle(page)
|
||||
after = [page.evaluate(_FILTER, tile), page.evaluate(_FILTER, tray)]
|
||||
page.goto(f"{base}/b/g/view?f=a.png", wait_until="networkidle")
|
||||
_settle(page)
|
||||
review = [page.evaluate(_FILTER, "#vimg"), page.evaluate(_FILTER, ".film-f.is-blurred img"),
|
||||
page.locator("[data-reveal-all]").inner_text()]
|
||||
fresh = ctx.new_page() # a new tab: sessionStorage is per tab
|
||||
fresh.goto(f"{base}/b/g/view?f=a.png", wait_until="networkidle")
|
||||
_settle(fresh)
|
||||
again = fresh.evaluate(_FILTER, "#vimg")
|
||||
ctx.close()
|
||||
assert all("blur" in f for f in before), before
|
||||
assert after == ["none", "none"], after
|
||||
assert review[:2] == ["none", "none"] and "blur again" in review[2], review
|
||||
assert "blur" in again, again
|
||||
|
||||
|
||||
def test_reveal_all_on_booth_a_does_not_reveal_booth_b_or_the_desk(browser, live):
|
||||
"""r2b D2 + INV-4: the reveal is scoped to the booth you are in. Booth A's
|
||||
cannot follow you into booth B, and nothing on the index is revealed."""
|
||||
base, root = live
|
||||
_blurred_set(root, "ga")
|
||||
_blurred_set(root, "gb")
|
||||
page = browser.new_page(viewport={"width": 1400, "height": 900})
|
||||
page.goto(f"{base}/b/ga/", wait_until="networkidle")
|
||||
page.locator("[data-reveal-all]").click()
|
||||
page.goto(f"{base}/b/gb/", wait_until="networkidle")
|
||||
_settle(page)
|
||||
b_tile = page.evaluate(_FILTER, 'figure.item[data-item="a.png"] img')
|
||||
page.goto(f"{base}/", wait_until="networkidle")
|
||||
desk = page.evaluate("""() => [...document.querySelectorAll('.desk-strip img.blurred-thumb')]
|
||||
.map(i => getComputedStyle(i).filter)""")
|
||||
page.close()
|
||||
assert "blur" in b_tile, b_tile
|
||||
assert desk and all("blur" in f for f in desk), desk
|
||||
|
||||
|
||||
def test_reveal_all_survives_an_in_place_save(browser, live):
|
||||
"""r2b D2: after an in-place save the blur is still lifted — the swapped-in
|
||||
tile included — the control still says "blur again" and still works, and
|
||||
the per-tile reveal buttons are still stood down."""
|
||||
base, root = live
|
||||
_blurred_set(root, blur=("a.png", "b.png"), flag=())
|
||||
page = browser.new_page(viewport={"width": 1400, "height": 900})
|
||||
page.goto(f"{base}/b/g/", wait_until="networkidle")
|
||||
page.evaluate("window.__same_page = 1")
|
||||
page.locator("[data-reveal-all]").click()
|
||||
page.locator('figure.item[data-item="b.png"] .flagtoggle button').click()
|
||||
page.wait_for_selector('figure.item.is-flagged[data-item="b.png"]', timeout=10000)
|
||||
_settle(page)
|
||||
got = {"same": page.evaluate("window.__same_page === 1"),
|
||||
"b": page.evaluate(_FILTER, 'figure.item[data-item="b.png"] img'),
|
||||
"label": page.locator("[data-reveal-all]").inner_text(),
|
||||
"tile_btn": page.evaluate("""() => getComputedStyle(
|
||||
document.querySelector('figure.item[data-item="b.png"] .reveal')).display""")}
|
||||
page.locator("[data-reveal-all]").click()
|
||||
_settle(page)
|
||||
got["back"] = page.evaluate(_FILTER, 'figure.item[data-item="b.png"] img')
|
||||
page.close()
|
||||
assert got["same"] and got["b"] == "none" and "blur again" in got["label"], got
|
||||
assert got["tile_btn"] == "none" and "blur" in got["back"], got
|
||||
|
||||
|
||||
def test_blur_again_restores_each_items_own_reveal(browser, live):
|
||||
"""r2b D2: Reveal all never touches an item's own reveal, so "blur again"
|
||||
returns each item exactly as it was — one revealed on its own stays so."""
|
||||
base, root = live
|
||||
_blurred_set(root, blur=("a.png", "b.png"), flag=())
|
||||
page = browser.new_page(viewport={"width": 1400, "height": 900})
|
||||
page.goto(f"{base}/b/g/", wait_until="networkidle")
|
||||
page.locator('figure.item[data-item="a.png"] .reveal').click()
|
||||
page.locator("[data-reveal-all]").click()
|
||||
page.locator("[data-reveal-all]").click()
|
||||
_settle(page)
|
||||
got = [page.evaluate(_FILTER, f'figure.item[data-item="{r}"] img') for r in ("a.png", "b.png")]
|
||||
page.close()
|
||||
assert got[0] == "none" and "blur" in got[1], got
|
||||
|
||||
|
||||
def test_reveal_all_never_shows_without_js_and_a_storage_failure_still_applies_the_click(browser, live):
|
||||
"""r2b D2: without JS the control is in the markup but never shown. With
|
||||
sessionStorage throwing on write (a private window), the click still
|
||||
applies to the page — only the memory is lost."""
|
||||
base, root = live
|
||||
_blurred_set(root)
|
||||
ctx = browser.new_context(java_script_enabled=False)
|
||||
page = ctx.new_page()
|
||||
page.goto(f"{base}/b/g/", wait_until="networkidle")
|
||||
nojs = page.locator("[data-reveal-all]").is_visible()
|
||||
ctx.close()
|
||||
page = browser.new_page(viewport={"width": 1400, "height": 900})
|
||||
page.add_init_script("Storage.prototype.setItem = function () { throw new Error('quota'); };")
|
||||
page.goto(f"{base}/b/g/", wait_until="networkidle")
|
||||
page.locator("[data-reveal-all]").click()
|
||||
_settle(page)
|
||||
lifted = page.evaluate(_FILTER, 'figure.item[data-item="a.png"] img')
|
||||
page.close()
|
||||
assert not nojs
|
||||
assert lifted == "none", lifted
|
||||
|
||||
Reference in New Issue
Block a user