fix(blur): the heid code-review and bug-hunt panels on r2b merge 1, folded
Both panels ran 4/4 on 5ded5ff. They converged on the board and doc-page
gaps independently.
- A board holding files lost both blur controls (they sat inside the
board suppression meant for the one-click wipe), while its items'
"◉ booth" labels pointed at them. Only the wipe is board-suppressed now.
- A blurred doc's own full page rendered clear. Its body is blurred there
too, with its own reveal and a Reveal all to put the blur back.
- set_booth_blurred followed a planted .blurbooth symlink (`touch`), and
the new control made that a click away. Anything at the name already
reads as fogged; otherwise it is created O_CREAT|O_EXCL|O_NOFOLLOW.
- The fog landing echoed `back` unchecked into the 303. It is now built
from the review ring, as the mark routes do.
- The fog form is its own region, so an in-place save refreshes its
label. Reveal all stays outside every region: its state lives in the
tab.
- The review's Space-to-advance no longer swallows Space on a focused
button or link.
- Top-bar controls stay on one line at phone width.
- Tests tightened:
- method="post" on the fog forms;
- exact blur values;
- a storage READ that throws;
- an item's own reveal carried across a swap;
- reveal gated where it can act.
r2b.toml: 26/26 proved. 774 passed.
This commit is contained in:
+127
-2
@@ -69,9 +69,9 @@ label = "D2 the control shows without JS (server markup not hidden)"
|
||||
file = "booth/templates/booth.html"
|
||||
test = "tests/test_flow_browser.py::test_reveal_all_never_shows_without_js_and_a_storage_failure_still_applies_the_click"
|
||||
old = '''
|
||||
{% if all_items | selectattr('blurred') | list %}<button type="button" class="reveal-all-btn" data-reveal-all hidden>'''
|
||||
{% if all_items | selectattr('blurred') | list %}<button type="button" class="reveal-all-btn" data-reveal-all hidden title='''
|
||||
new = '''
|
||||
{% if all_items | selectattr('blurred') | list %}<button type="button" class="reveal-all-btn" data-reveal-all>'''
|
||||
{% if all_items | selectattr('blurred') | list %}<button type="button" class="reveal-all-btn" data-reveal-all title='''
|
||||
|
||||
[[mutation]]
|
||||
label = "D2 the control is markup even when nothing is blurred"
|
||||
@@ -137,3 +137,128 @@ old = '''
|
||||
"blurred_self": it.rel in own_blur,'''
|
||||
new = '''
|
||||
"blurred_self": it.blurred,'''
|
||||
|
||||
# ---- folds: the heid code-review ("BLITZ-2") and bug-hunt ("FENRIR-6") panels on merge 1
|
||||
|
||||
[[mutation]]
|
||||
label = "a board holding files loses the blur controls its labels point at"
|
||||
file = "booth/templates/booth.html"
|
||||
test = "tests/test_flow.py::test_a_board_with_files_gets_the_blur_controls_its_labels_point_at"
|
||||
old = '''
|
||||
{% if all_items %}
|
||||
{# The fog form IS a region'''
|
||||
new = '''
|
||||
{% if not board %}
|
||||
{# The fog form IS a region'''
|
||||
|
||||
[[mutation]]
|
||||
label = "a blurred doc's own page renders clear"
|
||||
file = "booth/templates/doc.html"
|
||||
test = "tests/test_flow.py::test_a_blurred_docs_own_page_is_blurred_too"
|
||||
old = '''
|
||||
<div class="docbody{% if blurred %} is-blurred{% endif %}" id="docbody">'''
|
||||
new = '''
|
||||
<div class="docbody" id="docbody">'''
|
||||
|
||||
[[mutation]]
|
||||
label = "the review offers Reveal all when only a doc (off the ring) is blurred"
|
||||
file = "booth/templates/view.html"
|
||||
test = "tests/test_flow.py::test_reveal_all_renders_where_it_can_act"
|
||||
old = '''
|
||||
{% if film | selectattr('blurred') | list %}<button'''
|
||||
new = '''
|
||||
{% if true %}<button'''
|
||||
|
||||
[[mutation]]
|
||||
label = "the fog form is a GET (changes nothing with scripts off)"
|
||||
file = "booth/templates/booth.html"
|
||||
test = "tests/test_flow.py::test_the_booth_blur_toggle_works_without_js_and_lands_back_on_the_review"
|
||||
old = '''<form class="blur-all{% if booth_blurred %} is-on{% endif %}" method="post" action="/b/{{ name_url }}/blurbooth">
|
||||
<input type="hidden" name="on" value="{{ '0' if booth_blurred else '1' }}">
|
||||
<button title="{{ 'un-blur the whole booth — per-item'''
|
||||
new = '''<form class="blur-all{% if booth_blurred %} is-on{% endif %}" method="get" action="/b/{{ name_url }}/blurbooth">
|
||||
<input type="hidden" name="on" value="{{ '0' if booth_blurred else '1' }}">
|
||||
<button title="{{ 'un-blur the whole booth — per-item'''
|
||||
|
||||
[[mutation]]
|
||||
label = "the swap stops carrying an item's own reveal"
|
||||
file = "booth/templates/base.html"
|
||||
test = "tests/test_flow_browser.py::test_an_items_own_reveal_survives_an_in_place_save"
|
||||
old = '''
|
||||
['revealed', 'is-closed'].forEach(function (c) {'''
|
||||
new = '''
|
||||
['is-closed'].forEach(function (c) {'''
|
||||
|
||||
[[mutation]]
|
||||
label = "a storage READ that throws raises out of the pre-paint script"
|
||||
file = "booth/templates/base.html"
|
||||
test = "tests/test_flow_browser.py::test_reveal_all_never_shows_without_js_and_a_storage_failure_still_applies_the_click"
|
||||
old = '''
|
||||
try {
|
||||
if (b !== null && sessionStorage.getItem('booth.reveal:' + b) === '1') d.classList.add('reveal-all');
|
||||
} catch (e) {}'''
|
||||
new = '''
|
||||
if (b !== null && sessionStorage.getItem('booth.reveal:' + b) === '1') d.classList.add('reveal-all');'''
|
||||
|
||||
[[mutation]]
|
||||
label = "fogging writes through a planted marker link"
|
||||
file = "booth/app.py"
|
||||
test = "tests/test_flow.py::test_fogging_never_writes_through_a_planted_marker_link"
|
||||
old = '''
|
||||
try:
|
||||
os.lstat(marker)
|
||||
return True
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
try:
|
||||
os.close(os.open(marker, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o644))
|
||||
except FileExistsError:
|
||||
pass # lost a race to another fog: still fogged
|
||||
return True'''
|
||||
new = '''
|
||||
marker.touch(exist_ok=True)
|
||||
return True'''
|
||||
|
||||
[[mutation]]
|
||||
label = "the fog landing echoes `back` unchecked"
|
||||
file = "booth/app.py"
|
||||
test = "tests/test_flow.py::test_the_fog_landing_is_built_from_the_ring_never_echoed"
|
||||
old = '''
|
||||
if back and back in review_chain(booth_items(booth)):'''
|
||||
new = '''
|
||||
if back:'''
|
||||
|
||||
[[mutation]]
|
||||
label = "the fog form is outside every region (a swap leaves its label stale)"
|
||||
file = "booth/templates/booth.html"
|
||||
test = "tests/test_flow.py::test_the_booth_blur_toggle_works_without_js_and_lands_back_on_the_review"
|
||||
old = '''
|
||||
<span class="region-wrap" data-region="blur-booth"><form class="blur-all'''
|
||||
new = '''
|
||||
<span class="region-wrap"><form class="blur-all'''
|
||||
|
||||
[[mutation]]
|
||||
label = "Space on a focused review button moves to the next item"
|
||||
file = "booth/templates/view.html"
|
||||
test = "tests/test_flow_browser.py::test_space_on_a_focused_review_button_presses_it_and_does_not_move_on"
|
||||
old = '''e.target !== player && !(e.target.closest && e.target.closest('button, a, summary'))) {'''
|
||||
new = '''e.target !== player) {'''
|
||||
|
||||
[[mutation]]
|
||||
label = "the top-bar controls squeeze into multi-line stacks at phone width"
|
||||
file = "booth/templates/base.html"
|
||||
test = "tests/test_flow_browser.py::test_the_review_and_doc_top_bars_fit_a_phone"
|
||||
old = '''
|
||||
.blur-all button,.reveal-all-btn{white-space:nowrap}
|
||||
@media (max-width:600px){.reveal-all-btn .ra-note{display:none}}'''
|
||||
new = '''
|
||||
'''
|
||||
|
||||
[[mutation]]
|
||||
label = "the Desk strip under another booth's reveal is lifted by a whisker (blur(0px) is not blurred)"
|
||||
file = "booth/templates/base.html"
|
||||
test = "tests/test_flow_browser.py::test_reveal_all_on_booth_a_does_not_reveal_booth_b_or_the_desk"
|
||||
old = '''
|
||||
.blurred-thumb{filter:blur(16px)}'''
|
||||
new = '''
|
||||
.blurred-thumb{filter:blur(0px)}'''
|
||||
|
||||
+80
-1
@@ -861,8 +861,10 @@ def test_the_booth_blur_toggle_works_without_js_and_lands_back_on_the_review(tmp
|
||||
c = _client(tmp_path)
|
||||
page = c.get("/b/g/").text
|
||||
form = re.search(r'<form[^>]*action="/b/g/blurbooth".*?</form>', page, re.S).group(0)
|
||||
assert 'method="post"' in form, "a GET form would not change anything with scripts off"
|
||||
assert 'name="on" value="1"' in form and "blur booth" in form
|
||||
assert all(form not in r for r in _regions(page)), "a swap must never replace it"
|
||||
# a REGION: its label is server state, so an in-place save refreshes it
|
||||
assert form in _region(page, "blur-booth")
|
||||
assert "badge-blur" not in re.search(r'data-booth="g".*?</article>', c.get("/").text, re.S).group(0)
|
||||
|
||||
r = c.post("/b/g/blurbooth", data={"on": "1"}, follow_redirects=False)
|
||||
@@ -876,6 +878,7 @@ def test_the_booth_blur_toggle_works_without_js_and_lands_back_on_the_review(tmp
|
||||
view = c.get("/b/g/view?f=b.png").text
|
||||
vbar = re.search(r'<div class="vbar">.*?</div>\s*\n', view, re.S).group(0)
|
||||
vform = re.search(r'<form[^>]*action="/b/g/blurbooth".*?</form>', vbar, re.S).group(0)
|
||||
assert 'method="post"' in vform
|
||||
assert 'name="back" value="b.png"' in vform and 'name="on" value="0"' in vform
|
||||
r = c.post("/b/g/blurbooth", data={"on": "0", "back": "b.png"}, follow_redirects=False)
|
||||
assert r.headers["location"] == "/b/g/view?f=b.png" and not (b / ".blurbooth").exists()
|
||||
@@ -918,3 +921,79 @@ def test_under_a_fogged_booth_each_items_blur_control_tells_the_truth(tmp_path):
|
||||
assert re.search(r'action="/b/g/blur".*?name="on" value="0".*?◉ blurred', own, re.S)
|
||||
assert 'action="/b/g/blur"' not in booth
|
||||
assert "◉ booth" in booth
|
||||
|
||||
|
||||
|
||||
def test_a_board_with_files_gets_the_blur_controls_its_labels_point_at(tmp_path):
|
||||
"""heid code-review (4/4): both booth-wide controls sat inside the board
|
||||
suppression meant for the one-click wipe, so a links board holding a fogged
|
||||
picture showed "◉ booth — un-blur the booth in the header" with no such
|
||||
control in the header. Only the wipe is board-suppressed."""
|
||||
b = _booth(tmp_path, "links", {"links.md": b"- [x](https://example.test/)\n", "a.png": PNG})
|
||||
(b / ".blurbooth").write_bytes(b"")
|
||||
page = _client(tmp_path).get("/b/links/").text
|
||||
assert re.search(r'<form[^>]*action="/b/links/blurbooth"', page)
|
||||
assert re.search(r"<button[^>]*data-reveal-all", page)
|
||||
|
||||
|
||||
def test_reveal_all_renders_where_it_can_act(tmp_path):
|
||||
"""heid code-review (3/4): the header offers Reveal all when ANY item is
|
||||
blurred; the review only when an item of the review RING is — a blurred doc
|
||||
is not on the review page, so a control there would act on nothing."""
|
||||
from booth.app import set_blurred
|
||||
b = _booth(tmp_path, "g", {"a.png": PNG, "n.md": b"# n"})
|
||||
set_blurred(b, "n.md", True)
|
||||
c = _client(tmp_path)
|
||||
assert re.search(r"<button[^>]*data-reveal-all", c.get("/b/g/").text)
|
||||
assert not re.search(r"<button[^>]*data-reveal-all", c.get("/b/g/view?f=a.png").text)
|
||||
|
||||
|
||||
def test_a_blurred_docs_own_page_is_blurred_too(tmp_path):
|
||||
"""heid code-review (hulda): the full-page doc view never read `blurred`,
|
||||
so a blurred doc rendered clear at the size where it is most readable.
|
||||
Its body is blurred there too, with its own JS-only reveal."""
|
||||
from booth.app import set_blurred
|
||||
b = _booth(tmp_path, "g", {"n.md": b"# secret", "o.md": b"# open"})
|
||||
set_blurred(b, "n.md", True)
|
||||
c = _client(tmp_path)
|
||||
blurred, clear = c.get("/b/g/view?f=n.md").text, c.get("/b/g/view?f=o.md").text
|
||||
assert re.search(r'class="docbody is-blurred"', blurred)
|
||||
assert re.search(r'<button[^>]*id="docreveal"[^>]*hidden', blurred)
|
||||
assert 'class="docbody"' in clear and not re.search(r'<button[^>]*id="docreveal"', clear)
|
||||
|
||||
|
||||
def test_fogging_never_writes_through_a_planted_marker_link(tmp_path):
|
||||
"""heid bug-hunt (kimi, hulda): `marker.touch()` followed a planted
|
||||
`.blurbooth` symlink — a click of the new browser control rewrote an
|
||||
outside file's mtime, or CREATED a dangling target. The same class
|
||||
`record_view` was hardened against. A link already there reads as fogged
|
||||
(is_booth_blurred counts it), so fogging has nothing to write."""
|
||||
import os
|
||||
outside = tmp_path / "outside.txt"
|
||||
outside.write_text("x")
|
||||
os.utime(outside, (1_000_000, 1_000_000))
|
||||
b = _booth(tmp_path, "g", {"a.png": PNG})
|
||||
(b / ".blurbooth").symlink_to(outside)
|
||||
h = _booth(tmp_path, "h", {"a.png": PNG})
|
||||
(h / ".blurbooth").symlink_to(tmp_path / "created-by-a-click")
|
||||
c = _client(tmp_path)
|
||||
assert c.post("/b/g/blurbooth", data={"on": "1"}, follow_redirects=False).status_code == 303
|
||||
assert c.post("/b/h/blurbooth", data={"on": "1"}, follow_redirects=False).status_code == 303
|
||||
assert outside.stat().st_mtime == 1_000_000
|
||||
assert not (tmp_path / "created-by-a-click").exists()
|
||||
c.post("/b/g/blurbooth", data={"on": "0"}, follow_redirects=False)
|
||||
assert not (b / ".blurbooth").is_symlink() and outside.exists() # unlinked, target untouched
|
||||
|
||||
|
||||
def test_the_fog_landing_is_built_from_the_ring_never_echoed(tmp_path):
|
||||
"""heid bug-hunt (kimi, regin, groa): `back` went into the 303 unchecked, so
|
||||
a stale or foreign value landed on a 404. Like the mark routes' back=view:
|
||||
the review only for an item of the review ring, else the booth page."""
|
||||
_booth(tmp_path, "g", {"a.png": PNG, "n.md": b"# n"})
|
||||
c = _client(tmp_path)
|
||||
loc = lambda back: c.post("/b/g/blurbooth", data={"on": "1", "back": back},
|
||||
follow_redirects=False).headers["location"]
|
||||
assert loc("a.png") == "/b/g/view?f=a.png"
|
||||
assert loc("gone.png") == "/b/g/"
|
||||
assert loc("n.md") == "/b/g/"
|
||||
assert loc("") == "/b/g/"
|
||||
|
||||
@@ -618,8 +618,8 @@ def test_reveal_all_on_booth_a_does_not_reveal_booth_b_or_the_desk(browser, live
|
||||
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
|
||||
assert b_tile == "blur(22px)", b_tile
|
||||
assert desk and all(f == "blur(16px)" for f in desk), desk # exact: blur(0px) is not blurred
|
||||
|
||||
|
||||
def test_reveal_all_survives_an_in_place_save(browser, live):
|
||||
@@ -676,11 +676,100 @@ def test_reveal_all_never_shows_without_js_and_a_storage_failure_still_applies_t
|
||||
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'); };")
|
||||
errors = []
|
||||
page.on("pageerror", lambda e: errors.append(str(e)))
|
||||
# READS and WRITES both throw: the pre-paint read must degrade to "not
|
||||
# revealed" without raising, and the click must still apply.
|
||||
page.add_init_script("""Storage.prototype.setItem = function () { throw new Error('quota'); };
|
||||
Storage.prototype.getItem = function () { throw new Error('denied'); };""")
|
||||
page.goto(f"{base}/b/g/", wait_until="networkidle")
|
||||
_settle(page)
|
||||
before = page.evaluate(_FILTER, 'figure.item[data-item="a.png"] img')
|
||||
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
|
||||
assert before == "blur(22px)" and lifted == "none", (before, lifted)
|
||||
assert errors == [], errors
|
||||
|
||||
|
||||
|
||||
def test_an_items_own_reveal_survives_an_in_place_save(browser, live):
|
||||
"""heid code-review (groa, kimi): nothing pinned the swap carrying an item's
|
||||
own `revealed` — deleting it from the carry list left every test green.
|
||||
Reveal one tile, save something else in place: it stays revealed, and its
|
||||
button still says 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="b.png"] .reveal').click()
|
||||
page.locator('figure.item[data-item="a.png"] .flagtoggle button').click()
|
||||
page.wait_for_selector('figure.item.is-flagged[data-item="a.png"]', timeout=10000)
|
||||
_settle(page)
|
||||
got = [page.evaluate(_FILTER, 'figure.item[data-item="b.png"] img'),
|
||||
page.locator('figure.item[data-item="b.png"] .reveal').inner_text()]
|
||||
page.close()
|
||||
assert got[0] == "none" and "hide" in got[1], got
|
||||
|
||||
|
||||
def test_reveal_all_lifts_a_blurred_docs_own_page(browser, live):
|
||||
"""The doc page's blur (heid code-review) obeys the same <html> class, and
|
||||
its own reveal works."""
|
||||
from booth.app import set_blurred
|
||||
base, root = live
|
||||
b = root / "g"
|
||||
b.mkdir()
|
||||
(b / "n.md").write_text("# secret\n\nbody")
|
||||
set_blurred(b, "n.md", True)
|
||||
page = browser.new_page(viewport={"width": 1200, "height": 800})
|
||||
page.goto(f"{base}/b/g/view?f=n.md", wait_until="networkidle")
|
||||
_settle(page)
|
||||
at_rest = page.evaluate(_FILTER, "#docbody .markdown-body, #docbody .textview")
|
||||
page.locator("#docreveal").click()
|
||||
_settle(page)
|
||||
own = page.evaluate(_FILTER, "#docbody .markdown-body, #docbody .textview")
|
||||
page.close()
|
||||
assert at_rest == "blur(22px)" and own == "none", (at_rest, own)
|
||||
|
||||
|
||||
def test_space_on_a_focused_review_button_presses_it_and_does_not_move_on(browser, live):
|
||||
"""heid bug-hunt (hulda): the review's document-level Space handler moved to
|
||||
the next item before a focused button could take the key, so a keyboard
|
||||
user could not press Reveal all or the fog control with Space."""
|
||||
base, root = live
|
||||
_blurred_set(root, flag=())
|
||||
page = browser.new_page(viewport={"width": 1400, "height": 900})
|
||||
page.goto(f"{base}/b/g/view?f=a.png", wait_until="networkidle")
|
||||
page.locator("[data-reveal-all]").focus()
|
||||
page.keyboard.press(" ")
|
||||
_settle(page)
|
||||
got = [page.url, page.evaluate("document.documentElement.classList.contains('reveal-all')")]
|
||||
page.close()
|
||||
assert got[0].endswith("/b/g/view?f=a.png") and got[1], got
|
||||
|
||||
|
||||
def test_the_review_and_doc_top_bars_fit_a_phone(browser, live):
|
||||
"""heid bug-hunt (hulda, groa; needs-repro): the top bar gained the fog
|
||||
control and Reveal all beside the name, the fit toggle and the download.
|
||||
At 390px no review or doc page scrolls sideways, even with a long name."""
|
||||
from booth.app import set_blurred
|
||||
base, root = live
|
||||
b = _blurred_set(root, flag=())
|
||||
long = "a-rather-long-picture-name-" * 3 + ".png"
|
||||
(b / long).write_bytes(PNG)
|
||||
set_blurred(b, long, True)
|
||||
(b / "n.md").write_text("# n")
|
||||
set_blurred(b, "n.md", True)
|
||||
over = {}
|
||||
for url in (f"/b/g/view?f={long}", "/b/g/view?f=n.md"):
|
||||
page = browser.new_page(viewport={"width": 390, "height": 844})
|
||||
page.goto(f"{base}{url}", wait_until="networkidle")
|
||||
over[url] = page.evaluate(
|
||||
"document.documentElement.scrollWidth - document.documentElement.clientWidth")
|
||||
# and nothing squeezed into a stack: every top-bar control is one line
|
||||
over[url + " tallest"] = page.evaluate("""() => Math.max(...[...document.querySelectorAll(
|
||||
'.vbar button, .vbar .vbtn')].filter(e => e.offsetParent).map(e => e.getBoundingClientRect().height)) - 40""")
|
||||
page.close()
|
||||
assert all(v <= 0 for v in over.values()), over
|
||||
|
||||
Reference in New Issue
Block a user