fix(as-S5a): fixup from booth-dev's gate — release asks, fail-closed words, ids that cannot collide
From booth-dev's hulda bug-hunt with heid's second voice (BRINGA, thread 01M3MVGQ7QSCCK8WT59TQ4J469): - The booth page's "kept — release" asks by name, as the Desk's does. - WORDS has no prototype: data-confirm="__proto__" or "constructor" is an unknown word, and asks, instead of throwing before preventDefault. - The confirm helper moved into <head>: its capture listener exists before any form, so a click during load is asked too (the inline confirm() it replaced had that property). - shown() also marks U+2028/U+2029 and the zero-width characters. - Derived ids take ':' (bk-ask-<id>-<key>:prompt, bk-ask-<id>:title), which no id or key can contain; '-prompt' and '-title' collided with valid keys. booth-dev's chip test now looks its fragment up by [id=...]. - human_dur says "—" for a value that is not finite, instead of raising. - The tile's copy of a note drops its id (booth-dev: mark-<id> is the panel's article). - Four guards that asserted source patterns now also hold on computed effects: embed rings, rings inside clipping containers, the withdraw × on both axes, and question-level notes fields. Contract: as_antislop S5a (fixup). Falsifiers: antislop.toml 102/102 with r2_flow.toml 24/24 proved; the full gate follows.
This commit is contained in:
@@ -188,7 +188,8 @@ def test_withdraw_buttons_are_big_enough_to_hit(browser, live):
|
||||
ctx = browser.new_context(**ctx_args)
|
||||
page = ctx.new_page()
|
||||
page.goto(f"{base}/b/g/marks", wait_until="load") # visible at both widths
|
||||
box = page.locator(".mark-x").first.evaluate("e => e.getBoundingClientRect().height")
|
||||
box = page.locator(".mark-x").first.evaluate(
|
||||
"e => { const r = e.getBoundingClientRect(); return Math.min(r.width, r.height); }")
|
||||
assert box >= floor, (ctx_args, box)
|
||||
ctx.close()
|
||||
|
||||
@@ -231,3 +232,119 @@ def test_touch_and_scroll_behaviour(browser, live):
|
||||
"e => ({ws: getComputedStyle(e).whiteSpace, r: e.getBoundingClientRect().right, vw: innerWidth})")
|
||||
assert slug["ws"] == "normal" and slug["r"] <= slug["vw"], slug
|
||||
page.close()
|
||||
|
||||
|
||||
|
||||
# ---- S5a fixup: the confirm helper, and the rings on COMPUTED style ------------------------
|
||||
|
||||
def _dialog_texts(page):
|
||||
said = []
|
||||
page.on("dialog", lambda d: (said.append(d.message), d.dismiss()))
|
||||
return said
|
||||
|
||||
|
||||
def test_release_on_the_booth_page_asks_in_the_browser(browser, live):
|
||||
base, root = live
|
||||
b = root / "kept"
|
||||
b.mkdir()
|
||||
(b / "x.txt").write_text("x")
|
||||
(b / ".forever").write_text("")
|
||||
page = browser.new_page(viewport={"width": 1280, "height": 800})
|
||||
page.goto(f"{base}/b/kept/", wait_until="load")
|
||||
said = _dialog_texts(page)
|
||||
page.locator(".keep-lg button").click()
|
||||
page.wait_for_timeout(300)
|
||||
page.close()
|
||||
assert said and "“kept”" in said[0] and "Release" in said[0], said
|
||||
assert (b / ".forever").exists(), "dismissing must not release"
|
||||
|
||||
|
||||
def test_a_prototype_word_still_asks(browser, live):
|
||||
"""A `data-confirm` naming a property every object inherits is an unknown
|
||||
word, and an unknown word asks (fail closed)."""
|
||||
base, root = live
|
||||
b = root / "g"
|
||||
b.mkdir()
|
||||
(b / "x.txt").write_text("x")
|
||||
page = browser.new_page()
|
||||
page.goto(f"{base}/b/g/", wait_until="load")
|
||||
said = _dialog_texts(page)
|
||||
words = ["__proto__", "constructor", "toString", "hasOwnProperty", "valueOf"]
|
||||
for w in words:
|
||||
page.evaluate("""w => { const f = document.createElement('form');
|
||||
f.method = 'post'; f.action = '/b/g/delete';
|
||||
f.setAttribute('data-confirm', w); f.setAttribute('data-booth', 'g');
|
||||
document.body.appendChild(f); f.requestSubmit(); f.remove(); }""", w)
|
||||
page.wait_for_timeout(100)
|
||||
page.close()
|
||||
assert len(said) == len(words), said
|
||||
assert (b / "x.txt").exists()
|
||||
|
||||
|
||||
def test_the_dialog_shows_hidden_breaks_and_zero_widths_visibly(browser, live):
|
||||
import urllib.parse
|
||||
base, root = live
|
||||
name = "a
b
cde"
|
||||
b = root / name
|
||||
b.mkdir()
|
||||
(b / "x.txt").write_text("x")
|
||||
page = browser.new_page()
|
||||
page.goto(f"{base}/b/{urllib.parse.quote(name)}/", wait_until="load")
|
||||
said = _dialog_texts(page)
|
||||
page.locator(".wipe-lg button").click()
|
||||
page.wait_for_timeout(300)
|
||||
page.close()
|
||||
assert said, "positive control: Wipe now asked"
|
||||
for ch in "
":
|
||||
assert ch not in said[0], (hex(ord(ch)), said[0])
|
||||
assert said[0].count("�") == 5, said[0]
|
||||
|
||||
|
||||
_RING = """e => { const cs = getComputedStyle(e);
|
||||
return {fv: e.matches(':focus-visible'), style: cs.outlineStyle, width: cs.outlineWidth,
|
||||
color: cs.outlineColor, offset: cs.outlineOffset}; }"""
|
||||
|
||||
_ALPHA = """c => { const m = c.match(/rgba?\\(([^)]+)\\)/); const p = m ? m[1].split(',') : [];
|
||||
return p.length > 3 ? parseFloat(p[3]) : 1; }"""
|
||||
|
||||
|
||||
def _keyboard_focus(page, selector):
|
||||
page.keyboard.press("Shift") # keyboard modality: focus() is then :focus-visible
|
||||
loc = page.locator(selector).first
|
||||
loc.focus()
|
||||
return loc.evaluate(_RING)
|
||||
|
||||
|
||||
def test_rings_inside_clipping_containers_are_drawn_inside(browser, live):
|
||||
from booth.benches import upsert_bench
|
||||
base, root = live
|
||||
g = root / "g"
|
||||
g.mkdir()
|
||||
(g / "a.png").write_bytes(_png(64, 48))
|
||||
upsert_bench(root, "http://10.100.10.50:5173/", "peedlar desk", "peedlar-dev") # a Desk panel with a link
|
||||
page = browser.new_page(viewport={"width": 1280, "height": 800})
|
||||
for url, sel in (("/", ".theme button"), ("/", ".desk-panel a"), ("/b/g/", ".item a")):
|
||||
page.goto(base + url, wait_until="load")
|
||||
ring = _keyboard_focus(page, sel)
|
||||
assert ring["fv"] and ring["offset"] == "-2px", (url, sel, ring)
|
||||
page.close()
|
||||
|
||||
|
||||
def test_the_embed_draws_visible_rings(browser, live):
|
||||
from booth.marks import declare_pick
|
||||
base, root = live
|
||||
b = root / "r"
|
||||
b.mkdir()
|
||||
declare_pick(b, "winner", {"prompt": "Which?", "options": ["A", "B"]})
|
||||
(b / "index.html").write_text(
|
||||
'<!doctype html><html><head><script src="/_booth/embed.js" defer></script>'
|
||||
'<style>*:focus{outline:none}</style></head>' # a host that removes rings
|
||||
'<body><h1>Report</h1><div data-booth-ask="winner"></div></body></html>')
|
||||
page = browser.new_page()
|
||||
page.goto(f"{base}/b/r/", wait_until="networkidle")
|
||||
page.wait_for_selector(".bk-ask-go", timeout=10000)
|
||||
for sel in (".booth-nav-home", ".bk-ask-go"):
|
||||
ring = _keyboard_focus(page, sel)
|
||||
alpha = page.evaluate(_ALPHA, ring["color"])
|
||||
assert ring["fv"] and ring["style"] == "solid" and ring["width"] == "2px" and alpha == 1, (sel, ring)
|
||||
page.close()
|
||||
|
||||
Reference in New Issue
Block a user