fix(desk): the heid code-review and bug-hunt panels on r2b merge 2, folded
The bug hunt (4/4) and code review (4/4) were both clean on mechanism.
Their shared catch was the one-sided minute check.
Dates:
- The date filters never raise. One clock outside the calendar's range
500'd the Desk for every booth, because every row renders in one
response. An unrenderable date now renders nothing.
- "Updated" shows whenever it differs from "created" by a minute or more,
either way. Copied content is often older than its folder.
- A clock ahead of now shows its date, never "just now".
- A day is 24h ("1d ago" never appeared).
The row:
- The controls are last in the markup, so the booth's name comes first in
tab order and wipe last. The cluster is placed over the strip from the
row's box.
The theme:
- A choice made in one tab moves the Booth's other open tabs.
- The theme mark goes only on ask fragments the embed mounted.
Tests, strengthened after the code review:
- the pill is visible at rest;
- keyboard focus reveals the controls;
- the controls act with scripts off;
- Reveal all reaches the doc page;
- the high-contrast check reads tokens that actually differ;
- the art-light extras are written from SVOS, not derived from the
copies;
- two overstated mutation rows are replaced (one was a runtime no-op, one
went red through a syntax error).
Contract amended.
r2b.toml 55/55 proved. 799 passed.
This commit is contained in:
+121
-19
@@ -311,18 +311,18 @@ label = "D1 the cluster stays in flow where a hover exists (it takes room)"
|
||||
file = "booth/templates/base.html"
|
||||
test = "tests/test_flow_browser.py::test_the_row_controls_take_no_room_where_a_hover_exists"
|
||||
old = '''
|
||||
.desk-acts{position:absolute;top:5px;right:5px;flex:none;gap:4px;padding:3px;'''
|
||||
.desk-acts{position:absolute;top:17px;left:calc(12px + 210px - 5px);transform:translateX(-100%);flex:none;gap:4px;padding:3px;'''
|
||||
new = '''
|
||||
.desk-acts{position:static;top:5px;right:5px;flex:none;gap:4px;padding:3px;'''
|
||||
.desk-acts{position:static;top:17px;left:calc(12px + 210px - 5px);transform:translateX(-100%);flex:none;gap:4px;padding:3px;'''
|
||||
|
||||
[[mutation]]
|
||||
label = "D1 hover-only everywhere (no controls at all on touch)"
|
||||
file = "booth/templates/base.html"
|
||||
test = "tests/test_flow_browser.py::test_on_touch_the_row_controls_are_visible_in_flow_and_at_least_28px"
|
||||
old = '''
|
||||
.desk-acts{order:99;flex:1 0 100%;display:flex;flex-wrap:wrap;align-items:center;gap:6px}'''
|
||||
.desk-acts{flex:1 0 100%;display:flex;flex-wrap:wrap;align-items:center;gap:6px}'''
|
||||
new = '''
|
||||
.desk-acts{order:99;flex:1 0 100%;display:flex;flex-wrap:wrap;align-items:center;gap:6px;opacity:0}'''
|
||||
.desk-acts{flex:1 0 100%;display:flex;flex-wrap:wrap;align-items:center;gap:6px;opacity:0}'''
|
||||
|
||||
[[mutation]]
|
||||
label = "D1 touch controls fall below the 28px floor"
|
||||
@@ -338,35 +338,33 @@ label = "D1 zip back in the middle"
|
||||
file = "booth/templates/index.html"
|
||||
test = "tests/test_flow_browser.py::test_the_row_controls_run_zip_keep_or_release_then_wipe"
|
||||
old = '''
|
||||
<a class="dl-link" href="/b/{{ b.name_url }}/?download=1" title="download this booth as a zip">⬇ zip</a>
|
||||
{%- if b.kept %}
|
||||
<form class="release"'''
|
||||
<a class="dl-link" href="/b/{{ b.name_url }}/?download=1" title="download this booth as a zip">⬇ zip</a>
|
||||
{%- if b.kept %}
|
||||
<form class="release"'''
|
||||
new = '''
|
||||
{%- if b.kept %}
|
||||
<a class="dl-link" href="/b/{{ b.name_url }}/?download=1" title="download this booth as a zip">⬇ zip</a>
|
||||
<form class="release"'''
|
||||
{%- if b.kept %}
|
||||
<a class="dl-link" href="/b/{{ b.name_url }}/?download=1" title="download this booth as a zip">⬇ zip</a>
|
||||
<form class="release"'''
|
||||
|
||||
[[mutation]]
|
||||
label = "D1 a held booth's pill reads as counting down"
|
||||
file = "booth/templates/index.html"
|
||||
test = "tests/test_flow.py::test_the_lifetime_pill_class_is_kept_held_or_counting"
|
||||
old = '''('life-held' if b.hold in ('open', 'unreadable') else 'life-count')'''
|
||||
new = ''''life-count\''''
|
||||
new = '''('life-held' if b.hold == 'never' else 'life-count')'''
|
||||
|
||||
[[mutation]]
|
||||
label = "D1b an unknown birth time renders a guess"
|
||||
file = "booth/templates/_dates.html"
|
||||
test = "tests/test_flow.py::test_created_and_updated_are_dated_facts_and_none_says_nothing"
|
||||
old = '''
|
||||
{%- if created_at %} · <time class="d-made" datetime="{{ created_at|iso }}" title="created {{ created_at|stamp }}">created {{ created_at|day(now) }}</time>{% endif -%}'''
|
||||
new = '''
|
||||
{%- set created_at = created_at or landed_at %}{%- if created_at %} · <time class="d-made" datetime="{{ created_at|iso }}" title="created {{ created_at|stamp }}">created {{ created_at|day(now) }}</time>{% endif -%}'''
|
||||
old = '''{% macro dates(created_at, landed_at, now) -%}'''
|
||||
new = '''{% macro dates(created_at, landed_at, now) -%}{%- set created_at = created_at or landed_at -%}'''
|
||||
|
||||
[[mutation]]
|
||||
label = "D1b updated is measured from the wrong clock"
|
||||
file = "booth/templates/_dates.html"
|
||||
test = "tests/test_flow.py::test_created_and_updated_are_dated_facts_and_none_says_nothing"
|
||||
old = '''updated {{ (now - landed_at)|ago }}'''
|
||||
old = '''updated {{ age|ago }}'''
|
||||
new = '''updated {{ (now - created_at)|ago }}'''
|
||||
|
||||
[[mutation]]
|
||||
@@ -418,13 +416,18 @@ new = '''
|
||||
if (c === 'system')'''
|
||||
|
||||
[[mutation]]
|
||||
label = "D3 the stored theme is read after the stylesheet (a flash)"
|
||||
label = "D3 a stylesheet precedes the stored-theme script (a flash)"
|
||||
file = "booth/templates/base.html"
|
||||
test = "tests/test_flow.py::test_a_forced_theme_is_applied_before_first_paint"
|
||||
old = '''
|
||||
var t = localStorage.getItem('booth.theme');'''
|
||||
<script>
|
||||
(function () {
|
||||
var d = document.documentElement, b = d.getAttribute('data-booth');'''
|
||||
new = '''
|
||||
var t = localStorage.getItem('booth.' + 'theme');'''
|
||||
<style></style>
|
||||
<script>
|
||||
(function () {
|
||||
var d = document.documentElement, b = d.getAttribute('data-booth');'''
|
||||
|
||||
[[mutation]]
|
||||
label = "D3 the ask chrome in a verbatim page ignores the choice"
|
||||
@@ -444,3 +447,102 @@ old = '''
|
||||
if (e.key === "booth.theme" || e.key === null) bkTheme();'''
|
||||
new = '''
|
||||
if (false) bkTheme();'''
|
||||
|
||||
# ---- merge-2 folds: heid bug-hunt ("GORE-7") and code-review ("STAGGER-3")
|
||||
# Not expressible as one replacement, so pinned by their tests directly: the
|
||||
# row's DOM tab order (read in order by the test), and a declaration dropped
|
||||
# from BOTH light copies (the expected set is now written from SVOS, not
|
||||
# derived from the copies).
|
||||
|
||||
[[mutation]]
|
||||
label = "a date the calendar cannot hold raises through the Desk"
|
||||
file = "booth/app.py"
|
||||
test = "tests/test_flow.py::test_a_date_no_calendar_can_hold_renders_nothing_and_never_500s"
|
||||
old = '''
|
||||
_BAD_DATE = (OverflowError, OSError, ValueError)'''
|
||||
new = '''
|
||||
_BAD_DATE = ()'''
|
||||
|
||||
[[mutation]]
|
||||
label = "updated is dropped for content older than its booth (one-sided gap)"
|
||||
file = "booth/templates/_dates.html"
|
||||
test = "tests/test_flow.py::test_updated_shows_whenever_it_differs_from_created_and_a_future_one_says_its_date"
|
||||
old = '''(landed_at - created_at)|abs >= 60'''
|
||||
new = '''(landed_at - created_at) >= 60'''
|
||||
|
||||
[[mutation]]
|
||||
label = "a content clock ahead of now reads as an age"
|
||||
file = "booth/templates/_dates.html"
|
||||
test = "tests/test_flow.py::test_updated_shows_whenever_it_differs_from_created_and_a_future_one_says_its_date"
|
||||
old = '''{%- if age < -60 %}'''
|
||||
new = '''{%- if false %}'''
|
||||
|
||||
[[mutation]]
|
||||
label = "hours run on to 47h (1d ago never appears)"
|
||||
file = "booth/app.py"
|
||||
test = "tests/test_flow.py::test_an_age_is_said_in_its_largest_whole_unit"
|
||||
old = '''
|
||||
if s < 86400:
|
||||
return f"{s // 3600}h ago"'''
|
||||
new = '''
|
||||
if s < 2 * 86400:
|
||||
return f"{s // 3600}h ago"'''
|
||||
|
||||
[[mutation]]
|
||||
label = "a theme chosen in one tab does not reach the Booth's other open tabs"
|
||||
file = "booth/templates/base.html"
|
||||
test = "tests/test_flow_browser.py::test_a_theme_chosen_in_one_tab_moves_the_others"
|
||||
old = '''
|
||||
if (e.key !== 'booth.theme' && e.key !== null) return;
|
||||
var t = null;'''
|
||||
new = '''
|
||||
return;
|
||||
var t = null;'''
|
||||
|
||||
[[mutation]]
|
||||
label = "the theme mark reaches the author's own .bk-ask"
|
||||
file = "booth/static/embed.js"
|
||||
test = "tests/test_flow_browser.py::test_the_theme_marks_only_the_ask_fragments_we_mounted"
|
||||
old = '''
|
||||
ours.forEach(function (root) {'''
|
||||
new = '''
|
||||
[document.body].forEach(function (root) {'''
|
||||
|
||||
[[mutation]]
|
||||
label = "keyboard focus no longer reveals the row's controls"
|
||||
file = "booth/templates/base.html"
|
||||
test = "tests/test_flow_browser.py::test_the_pill_shows_at_rest_and_focus_reveals_the_controls"
|
||||
old = '''
|
||||
.desk-row:hover .desk-acts,.desk-row:focus-within .desk-acts{opacity:1;pointer-events:auto}'''
|
||||
new = '''
|
||||
.desk-row:hover .desk-acts{opacity:1;pointer-events:auto}'''
|
||||
|
||||
[[mutation]]
|
||||
label = "the lifetime pill hides at rest"
|
||||
file = "booth/templates/base.html"
|
||||
test = "tests/test_flow_browser.py::test_the_pill_shows_at_rest_and_focus_reveals_the_controls"
|
||||
old = '''
|
||||
.life-count::before{content:"◷"}'''
|
||||
new = '''
|
||||
.life-count::before{content:"◷"}
|
||||
.life{opacity:0}'''
|
||||
|
||||
[[mutation]]
|
||||
label = "high contrast never applies (dark-hc block gone)"
|
||||
file = "booth/templates/_svos_tokens.css"
|
||||
test = "tests/test_flow_browser.py::test_a_forced_theme_follows_high_contrast"
|
||||
old = '''
|
||||
/* dark high contrast: whenever dark is in effect (light, below, outranks it) */
|
||||
@media (prefers-contrast: more) {'''
|
||||
new = '''
|
||||
/* dark high contrast: whenever dark is in effect (light, below, outranks it) */
|
||||
@media (prefers-contrast: less) {'''
|
||||
|
||||
[[mutation]]
|
||||
label = "Reveal all does not reach a blurred doc's own page"
|
||||
file = "booth/templates/doc.html"
|
||||
test = "tests/test_flow_browser.py::test_reveal_all_lifts_the_doc_page_it_reaches"
|
||||
old = '''
|
||||
.reveal-all .docbody.is-blurred .markdown-body,.reveal-all .docbody.is-blurred .textview{filter:none}'''
|
||||
new = '''
|
||||
.reveal-all-OFF .docbody.is-blurred .markdown-body,.reveal-all .docbody.is-blurred .textview{filter:none}'''
|
||||
|
||||
+55
-2
@@ -1045,8 +1045,13 @@ def test_a_forced_theme_and_the_os_theme_are_the_same_declarations():
|
||||
assert "color-scheme: dark" in dark and len(dark) > 30 # the parser found the real dark block
|
||||
assert os_light == forced_light
|
||||
assert os_lhc == forced_lhc
|
||||
assert props(forced_light) == props(dark) | (props(forced_light) & props(art))
|
||||
assert props(dark) <= props(forced_light)
|
||||
# Light overrides every dark property plus the art layer's four light-only
|
||||
# values. That extra set is written here, from SVOS, NOT derived from the
|
||||
# copies: deriving it from them let a transform that dropped an art-light
|
||||
# value from BOTH copies pass (heid code-review, 2/4).
|
||||
art_light = {"--shadow-sm", "--shadow-md", "--shadow-lg", "--glow-armed"}
|
||||
assert art_light <= props(art)
|
||||
assert props(forced_light) == props(dark) | art_light
|
||||
assert props(forced_lhc) == props(dhc)
|
||||
|
||||
|
||||
@@ -1064,11 +1069,14 @@ def test_the_lifetime_pill_class_is_kept_held_or_counting(tmp_path):
|
||||
h = _booth(tmp_path, "held", {"a.png": PNG})
|
||||
declare_pick(h, "q", {"prompt": "Which?", "options": ["x", "y"]})
|
||||
_booth(tmp_path, "loose", {"a.png": PNG})
|
||||
broken = _booth(tmp_path, "broken", {"a.png": PNG})
|
||||
(broken / ".marks.json").write_text("{not json")
|
||||
body = _client(tmp_path).get("/").text
|
||||
pill = lambda n: re.search(r'<span class="life (life-\w+)"[^>]*>(.*?)</span>\s*</div>', _row(body, n), re.S)
|
||||
assert pill("kept1").group(1) == "life-kept" and "kept" in pill("kept1").group(2)
|
||||
assert pill("held").group(1) == "life-held" and "held until answered" in pill("held").group(2)
|
||||
assert pill("loose").group(1) == "life-count" and "expires in" in pill("loose").group(2)
|
||||
assert pill("broken").group(1) == "life-held" and "marks unreadable" in pill("broken").group(2)
|
||||
# and the lifetime left the facts line, which is facts only
|
||||
assert "expires in" not in re.search(r'class="desk-facts".*?</div>', _row(body, "loose"), re.S).group(0)
|
||||
|
||||
@@ -1108,3 +1116,48 @@ def test_a_forced_theme_is_applied_before_first_paint(tmp_path):
|
||||
head = page[:page.index("</head>")]
|
||||
assert head.index("booth.theme") < head.index("<style>"), url
|
||||
assert re.search(r'<div class="theme"[^>]*hidden', page), url
|
||||
|
||||
|
||||
def test_a_date_no_calendar_can_hold_renders_nothing_and_never_500s(tmp_path, monkeypatch):
|
||||
"""heid bug-hunt (4/4): the date filters raised on a timestamp outside the
|
||||
calendar's range, and the Desk renders every row in one response — one
|
||||
poisoned clock 500'd the index for every booth. A date that cannot be
|
||||
rendered renders nothing, like a birth time the disk cannot give."""
|
||||
import booth.app as app_mod
|
||||
_booth(tmp_path, "g", {"a.png": PNG})
|
||||
_booth(tmp_path, "ok", {"a.png": PNG})
|
||||
monkeypatch.setattr(app_mod, "birth_time", lambda p: 1e20 if p.name == "g" else None)
|
||||
monkeypatch.setattr(app_mod, "_content_mtime", lambda p: -1e20 if p.name == "g" else time.time() - 3600)
|
||||
c = _client(tmp_path)
|
||||
r = c.get("/")
|
||||
assert r.status_code == 200
|
||||
assert "created" not in re.search(r'class="desk-facts".*?</div>', _row(r.text, "g"), re.S).group(0)
|
||||
assert c.get("/b/g/").status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.parametrize("age,words", [(30, "just now"), (59 * 60, "59m ago"), (23 * 3600, "23h ago"),
|
||||
(30 * 3600, "1d ago"), (47 * 3600, "1d ago"), (5 * 86400, "5d ago")])
|
||||
def test_an_age_is_said_in_its_largest_whole_unit(age, words):
|
||||
"""heid bug-hunt (groa): hours ran on to 47h, so "1d ago" never appeared,
|
||||
against the helper's own docstring."""
|
||||
from booth.app import date_ago
|
||||
assert date_ago(age) == words
|
||||
|
||||
|
||||
def test_updated_shows_whenever_it_differs_from_created_and_a_future_one_says_its_date(tmp_path, monkeypatch):
|
||||
"""heid bug-hunt (hulda, groa): "updated" was dropped for content OLDER than
|
||||
the booth — copied files keep their mtimes while the folder is born now —
|
||||
and a future content clock read "updated just now". Either direction of a
|
||||
real gap shows; a clock ahead of now shows its date, never an age."""
|
||||
import booth.app as app_mod
|
||||
now = time.time()
|
||||
_booth(tmp_path, "copied", {"a.png": PNG})
|
||||
_booth(tmp_path, "ahead", {"a.png": PNG})
|
||||
monkeypatch.setattr(app_mod, "birth_time", lambda p: now - 3600)
|
||||
monkeypatch.setattr(app_mod, "_content_mtime",
|
||||
lambda p: now - 30 * 86400 if p.name == "copied" else now + 5 * 86400)
|
||||
body = _client(tmp_path).get("/").text
|
||||
facts = lambda n: re.search(r'class="desk-facts".*?</div>', _row(body, n), re.S).group(0)
|
||||
assert re.search(r">updated 30d ago</time>", facts("copied"))
|
||||
ahead = facts("ahead")
|
||||
assert "just now" not in ahead and re.search(r">updated \d{1,2} [A-Z][a-z]{2}( \d{4})?</time>", ahead)
|
||||
|
||||
+123
-1
@@ -898,11 +898,19 @@ def test_a_forced_theme_follows_high_contrast(browser, live):
|
||||
cdp.send("Emulation.setEmulatedMedia", {"features": [
|
||||
{"name": "prefers-contrast", "value": "more"},
|
||||
{"name": "prefers-color-scheme", "value": os_scheme}]})
|
||||
got[(os_scheme, forced)] = page.evaluate(_VAR, "--surface-card")
|
||||
# tokens that DIFFER between a theme and its high-contrast variant
|
||||
# (--surface-card does not, so reading it saw nothing — heid, 2/4)
|
||||
got[(os_scheme, forced)] = (page.evaluate(_VAR, "--text-faint"), page.evaluate(_VAR, "--border-default"))
|
||||
if forced is None and os_scheme == "dark":
|
||||
cdp.send("Emulation.setEmulatedMedia", {"features": [
|
||||
{"name": "prefers-contrast", "value": "no-preference"},
|
||||
{"name": "prefers-color-scheme", "value": os_scheme}]})
|
||||
got["dark, no contrast"] = (page.evaluate(_VAR, "--text-faint"), page.evaluate(_VAR, "--border-default"))
|
||||
ctx.close()
|
||||
assert got[("light", "dark")] == got[("dark", None)] == got[("dark", "dark")]
|
||||
assert got[("dark", "light")] == got[("light", None)] == got[("light", "light")]
|
||||
assert got[("dark", None)] != got[("light", None)]
|
||||
assert got[("dark", None)] != got["dark, no contrast"] # high contrast really applied
|
||||
|
||||
|
||||
def test_the_theme_toggle_never_shows_without_js_and_a_storage_failure_still_applies(browser, live):
|
||||
@@ -961,3 +969,117 @@ def test_a_forced_theme_reaches_the_ask_chrome_inside_a_verbatim_page(browser, l
|
||||
ctx.close()
|
||||
assert os_dark == "#b2cd12" and forced_light == "#586519" and after_reload == "#586519", (os_dark, forced_light, after_reload)
|
||||
assert host_html is None
|
||||
|
||||
|
||||
def test_a_rows_booth_name_comes_before_its_controls_in_tab_order(browser, live):
|
||||
"""heid bug-hunt (groa): the cluster sat before the text in the markup, so
|
||||
the first tab stop on a row was zip, then keep, then WIPE — before the booth
|
||||
it acts on — and with scripts off, wipe submits with no confirm. The name
|
||||
is first; the controls follow, wipe last."""
|
||||
base, root = live
|
||||
_two_rows(root)
|
||||
ctx = browser.new_context(viewport={"width": 390, "height": 844}, has_touch=True, is_mobile=True)
|
||||
page = ctx.new_page()
|
||||
page.goto(f"{base}/", wait_until="networkidle")
|
||||
order = page.evaluate("""() => { const row = document.querySelector('.desk-row[data-booth="loose"]');
|
||||
return [...row.querySelectorAll('a[href], button')].filter(e => e.tabIndex >= 0)
|
||||
.map(e => e.classList.contains('desk-title') ? 'title' : e.closest('form') ? e.closest('form').className.split(' ')[0] : e.className.split(' ')[0]); }""")
|
||||
ctx.close()
|
||||
assert order == ["title", "dl-link", "keepit", "wipe"], order
|
||||
|
||||
|
||||
def test_a_theme_chosen_in_one_tab_moves_the_others(browser, live):
|
||||
"""heid bug-hunt (groa): the ask chrome followed a choice made in another
|
||||
tab, but the Booth's own open pages did not until reloaded."""
|
||||
base, root = live
|
||||
_set(root, 1)
|
||||
ctx = browser.new_context(color_scheme="dark")
|
||||
a, b = ctx.new_page(), ctx.new_page()
|
||||
a.goto(f"{base}/", wait_until="networkidle")
|
||||
b.goto(f"{base}/b/g/", wait_until="networkidle")
|
||||
a.locator('.theme [data-theme-choice="light"]').click()
|
||||
b.wait_for_function("document.documentElement.getAttribute('data-theme') === 'light'", timeout=5000)
|
||||
pressed = b.locator('.theme [aria-pressed="true"]').get_attribute("data-theme-choice")
|
||||
a.locator('.theme [data-theme-choice="system"]').click()
|
||||
b.wait_for_function("!document.documentElement.hasAttribute('data-theme')", timeout=5000)
|
||||
ctx.close()
|
||||
assert pressed == "light"
|
||||
|
||||
|
||||
def test_the_theme_marks_only_the_ask_fragments_we_mounted(browser, live):
|
||||
"""heid bug-hunt (regin): the theme mark went on every `.bk-ask` in the
|
||||
page, the author's own included. Only fragments the embed mounted."""
|
||||
from booth.marks import declare_pick
|
||||
base, root = live
|
||||
b = root / "rep"
|
||||
b.mkdir()
|
||||
declare_pick(b, "winner", {"prompt": "Which?", "options": ["A", "B"]})
|
||||
(b / "index.html").write_text('<!doctype html><title>r</title><body><div class="bk-ask" id="authors">mine</div>'
|
||||
'<script src="/_booth/embed.js" defer></script></body>')
|
||||
ctx = browser.new_context()
|
||||
page = ctx.new_page()
|
||||
page.add_init_script("try { localStorage.setItem('booth.theme', 'light'); } catch (e) {}")
|
||||
page.goto(f"{base}/b/rep/", wait_until="networkidle")
|
||||
page.wait_for_selector(".bk-ask:not(#authors)")
|
||||
got = page.evaluate("""() => [document.getElementById('authors').getAttribute('data-bk-theme'),
|
||||
[...document.querySelectorAll('.bk-ask:not(#authors)')].map(e => e.getAttribute('data-bk-theme'))]""")
|
||||
ctx.close()
|
||||
assert got[0] is None and got[1] and all(t == "light" for t in got[1]), got
|
||||
|
||||
|
||||
def test_the_pill_shows_at_rest_and_focus_reveals_the_controls(browser, live):
|
||||
"""heid code-review: the pill's "visible with no hover" was never asserted
|
||||
(an opacity-0 box keeps its geometry), and nothing pinned KEYBOARD focus
|
||||
revealing the controls — `:focus-within` could go alone."""
|
||||
base, root = live
|
||||
_two_rows(root)
|
||||
page = browser.new_page(viewport={"width": 1400, "height": 900})
|
||||
page.goto(f"{base}/", wait_until="networkidle")
|
||||
page.mouse.move(0, 0)
|
||||
row = page.locator('.desk-row[data-booth="loose"]')
|
||||
pill = row.locator(".life").evaluate("e => [getComputedStyle(e).opacity, getComputedStyle(e).visibility, e.offsetWidth > 0]")
|
||||
row.locator(".desk-acts .dl-link").focus()
|
||||
page.wait_for_timeout(350)
|
||||
focused = row.locator(".desk-acts").evaluate("a => [getComputedStyle(a).opacity, getComputedStyle(a).pointerEvents]")
|
||||
page.close()
|
||||
assert pill == ["1", "visible", True], pill
|
||||
assert focused == ["1", "auto"], focused
|
||||
|
||||
|
||||
def test_with_scripts_off_a_rows_controls_still_act(browser, live):
|
||||
"""r2b INV-2, never exercised with JS actually off (heid code-review): the
|
||||
hover reveal is CSS, and keep is a plain form."""
|
||||
base, root = live
|
||||
_two_rows(root)
|
||||
ctx = browser.new_context(java_script_enabled=False, viewport={"width": 1400, "height": 900})
|
||||
page = ctx.new_page()
|
||||
page.goto(f"{base}/", wait_until="networkidle")
|
||||
row = page.locator('.desk-row[data-booth="loose"]')
|
||||
row.hover()
|
||||
page.wait_for_timeout(350)
|
||||
with page.expect_navigation():
|
||||
row.locator(".desk-acts form.keepit button").click()
|
||||
ctx.close()
|
||||
assert (root / "loose" / ".forever").exists()
|
||||
|
||||
|
||||
def test_reveal_all_lifts_the_doc_page_it_reaches(browser, live):
|
||||
"""heid code-review (regin, seat-settled): the doc page carries the rules,
|
||||
but no test turned Reveal all on and looked there."""
|
||||
from booth.app import set_blurred
|
||||
base, root = live
|
||||
b = root / "g"
|
||||
b.mkdir()
|
||||
(b / "a.png").write_bytes(PNG)
|
||||
(b / "n.md").write_text("# secret\n\nbody")
|
||||
set_blurred(b, "a.png", True)
|
||||
set_blurred(b, "n.md", True)
|
||||
page = browser.new_page(viewport={"width": 1200, "height": 800})
|
||||
page.goto(f"{base}/b/g/", wait_until="networkidle")
|
||||
page.locator("[data-reveal-all]").click()
|
||||
page.goto(f"{base}/b/g/view?f=n.md", wait_until="networkidle")
|
||||
_settle(page)
|
||||
got = [page.evaluate(_FILTER, "#docbody .markdown-body, #docbody .textview"),
|
||||
page.evaluate("getComputedStyle(document.getElementById('docreveal')).display")]
|
||||
page.close()
|
||||
assert got == ["none", "none"], got
|
||||
|
||||
Reference in New Issue
Block a user