feat(booth): asks render INLINE in a verbatim report, placed by the author
Operator verdict on the separate /asks page: the question belongs with the artifact it is about. A four-voice audition wants each voice's radio group under that voice's audio, and one submit for the lot. - booth/inline.py: data-booth-ask="stem" | "stem:key" | data-booth-ask-submit, plus <!-- booth:ask ... --> comments; unknown stem left alone, not blanked - _ask_inline.html: self-contained fragments (own scoped styles, no JS), per-question groups bound to one form via the HTML5 form= attribute so a scattered multi-question ask still POSTs once - unplaced questions and a missing submit block are appended, so a partially marked-up page can never produce an unsubmittable 400 - chip becomes a jump link to the first open ask; /asks page kept as a fallback - 6 tests (one caught the partial-placement drop); v0.1.14
This commit is contained in:
@@ -318,15 +318,17 @@ def test_write_ask_accepts_full_doc(tmp_path):
|
||||
# into the verbatim page plus a standalone /asks page that carries the forms.
|
||||
|
||||
|
||||
def test_verbatim_booth_gets_an_asks_chip(client):
|
||||
def test_verbatim_booth_renders_the_ask_inline(client):
|
||||
c, data = client
|
||||
b = _ask(data / "b")
|
||||
(b / "index.html").write_text("<!doctype html><title>report</title><body>hi</body>")
|
||||
html = c.get("/b/b/").text
|
||||
assert "hi" in html # the report is still served verbatim
|
||||
assert "booth-nav-asks" in html # ...with a chip pointing at the asks
|
||||
assert "1 open ask" in html
|
||||
assert "/b/b/asks" in html
|
||||
assert "hi" in html # the report is still served verbatim
|
||||
assert "Which render wins?" in html # ...with the ask ON it, not elsewhere
|
||||
assert 'type="radio"' in html and 'action="/b/b/answer"' in html
|
||||
assert "bk-ask" in html # self-contained fragment styles
|
||||
assert "booth-nav-asks" in html # chip remains, as a jump link
|
||||
assert "#bk-ask-winner-top" in html
|
||||
|
||||
|
||||
def test_verbatim_chip_disappears_once_answered(client):
|
||||
@@ -381,3 +383,87 @@ def test_asks_page_shows_a_single_ask_title(client):
|
||||
c, data = client
|
||||
_ask(data / "b", title="emmie — pick the anchor")
|
||||
assert "emmie — pick the anchor" in c.get("/b/b/asks").text
|
||||
|
||||
|
||||
# ---- inline placement in a verbatim report -----------------------------------
|
||||
#
|
||||
# Operator verdict 2026-09-09 on the separate /asks page: "the asks should be
|
||||
# inline with the artifacts, not on a separate page." A four-voice audition wants
|
||||
# each voice's radio group under that voice's audio, and one submit for the lot.
|
||||
|
||||
|
||||
REPORT = """<!doctype html><title>audition</title><body>
|
||||
<h1>Three voices</h1>
|
||||
<section id="lawson"><audio src="a.wav"></audio>
|
||||
<div data-booth-ask="batch:r1"></div></section>
|
||||
<section id="jo"><audio src="b.wav"></audio>
|
||||
<!-- booth:ask batch:r2 --></section>
|
||||
<div data-booth-ask-submit="batch"></div>
|
||||
</body>"""
|
||||
|
||||
|
||||
def test_per_question_placeholders_land_where_the_author_put_them(client):
|
||||
c, data = client
|
||||
b = _multi(data / "b")
|
||||
(b / "index.html").write_text(REPORT)
|
||||
html = c.get("/b/b/").text
|
||||
# each group is inside its own section, in document order
|
||||
lawson = html.index('id="lawson"')
|
||||
jo = html.index('id="jo"')
|
||||
assert lawson < html.index('name="choice.r1"') < jo
|
||||
assert jo < html.index('name="choice.r2"')
|
||||
# one shared form, bound by the HTML5 form= attribute, submitted once
|
||||
assert html.count('<form id="bk-ask-form-batch"') == 1
|
||||
assert html.count('action="/b/b/answer"') == 1
|
||||
assert html.count('form="bk-ask-form-batch"') >= 4
|
||||
# the submit block landed at its own placeholder, not appended after </body>
|
||||
assert html.index("bk-ask-form-batch") < html.index("</body>")
|
||||
|
||||
|
||||
def test_inline_form_submits_every_question_in_one_post(client):
|
||||
c, data = client
|
||||
b = _multi(data / "b")
|
||||
(b / "index.html").write_text(REPORT)
|
||||
r = c.post("/b/b/answer", data={"ask": "batch", "choice.r1": "keep", "notes.r1": "crisp",
|
||||
"choice.r2": "d", "notes": "ship r1"}, follow_redirects=False)
|
||||
assert r.status_code == 303
|
||||
ans = read_answer(b, "batch")
|
||||
assert ans["answers"]["r1"]["choice"] == "keep" and ans["answers"]["r2"]["choice"] == "d"
|
||||
# and the recorded pick now shows inline, on the report itself
|
||||
html = c.get("/b/b/").text
|
||||
assert "recorded:" in html and "bk-done" in html
|
||||
assert 'value="keep" required checked' in html.replace("\n", " ") or "checked" in html
|
||||
|
||||
|
||||
def test_whole_ask_placeholder_renders_everything_there(client):
|
||||
c, data = client
|
||||
b = _ask(data / "b")
|
||||
(b / "index.html").write_text('<!doctype html><body><p>x</p><div data-booth-ask="winner"></div></body>')
|
||||
html = c.get("/b/b/").text
|
||||
assert html.index("Which render wins?") > html.index("<p>x</p>")
|
||||
assert html.index("bk-ask-go") < html.index("</body>") # submit placed inline too
|
||||
|
||||
|
||||
def test_placeholder_for_a_missing_ask_is_left_alone(client):
|
||||
c, data = client
|
||||
b = _ask(data / "b")
|
||||
(b / "index.html").write_text('<!doctype html><body><div data-booth-ask="typo"></div></body>')
|
||||
html = c.get("/b/b/").text
|
||||
assert 'data-booth-ask="typo"' in html # author's markup untouched, not blanked
|
||||
assert "Which render wins?" in html # the real ask still appended, never lost
|
||||
|
||||
|
||||
def test_questions_placed_without_a_submit_still_get_one(client):
|
||||
c, data = client
|
||||
b = _multi(data / "b")
|
||||
(b / "index.html").write_text('<!doctype html><body><div data-booth-ask="batch:r1"></div></body>')
|
||||
html = c.get("/b/b/").text
|
||||
assert html.count('<form id="bk-ask-form-batch"') == 1 # appended, so it is submittable
|
||||
assert 'name="choice.r2"' in html # r2 unplaced -> must still appear
|
||||
|
||||
|
||||
def test_styles_are_emitted_once(client):
|
||||
c, data = client
|
||||
b = _multi(data / "b")
|
||||
(b / "index.html").write_text(REPORT)
|
||||
assert c.get("/b/b/").text.count(".bk-ask-opt:has(input:checked)") == 1
|
||||
|
||||
Reference in New Issue
Block a user