feat: visually review mobile issue evidence (Closes #835)
CI / lint (pull_request) Successful in 1m44s
CI / build-release (pull_request) Successful in 6s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-14 15:56:08 +00:00
1 parent a500915223
commit d797179844
5 files changed
+221 -2

No files matched your search

+129
View File
@@ -99,6 +99,122 @@ process.stdout.write(JSON.stringify({{hidden:sheet.hidden,focused:trigger.focuse
assert run_node(script) == {"hidden": True, "focused": True, "title": "Original"}
def test_review_renders_exact_evidence_bytes_and_selects_the_preview():
script = f"""
const createReview = require({json.dumps(str(MODULE))});
function target(tag='div') {{
const listeners={{}}, attributes={{}};
return {{tag,hidden:true,disabled:false,textContent:'',children:[],src:'',alt:'',
addEventListener:(name,fn)=>listeners[name]=fn,
dispatch:(name,event={{}})=>listeners[name]?.({{preventDefault(){{}},currentTarget:this,...event}}),
replaceChildren(...items){{this.children=items;}}, appendChild(item){{this.children.push(item);}},
setAttribute(name,value){{attributes[name]=String(value);}}, getAttribute:name=>attributes[name],
focus(){{this.focused=true;}}}};
}}
const documentRef={{createElement:tag=>target(tag),addEventListener:()=>{{}}}};
const evidence=target(), preview=target(), image=target('img');
const position=target(), filename=target(), note=target(), empty=target();
const created=[];
const review=createReview({{
sheet:target(),confirmButton:target(),backButton:target(),evidenceList:evidence,
evidencePreview:preview,evidenceImage:image,evidencePosition:position,
evidenceFilename:filename,evidenceNote:note,evidenceEmpty:empty,
repository:target(),intent:target(),title:target(),body:target(),metadata:target(),
document:documentRef,createObjectURL:blob=>{{const url='blob:review-'+blob.size;created.push(url);return url;}},
revokeObjectURL:()=>{{}},onConfirm:async()=>{{}},
}});
review.open({{draft:{{repository:'o/r',title:'Privacy check',attachments:[
{{filename:'redacted.png',contentType:'image/png',blob:new Blob(['flattened-redaction']),note:'Secrets hidden'}},
{{filename:'legacy.webp',contentType:'image/webp',data:'bGVnYWN5',note:'Expected result'}}
]}},intent:'create-and-assign'}},target());
const firstButton=evidence.children[0].children[0];
const secondButton=evidence.children[1].children[0];
secondButton.dispatch('click');
process.stdout.write(JSON.stringify({{
created,thumbnailSources:[firstButton.children[0].src,secondButton.children[0].src],
labels:[firstButton.getAttribute('aria-label'),secondButton.getAttribute('aria-label')],
selected:[firstButton.getAttribute('aria-pressed'),secondButton.getAttribute('aria-pressed')],
preview:{{hidden:preview.hidden,src:image.src,alt:image.alt,position:position.textContent,
filename:filename.textContent,note:note.textContent}},emptyHidden:empty.hidden,
}}));
"""
result = run_node(script)
assert result == {
"created": ["blob:review-19"],
"thumbnailSources": [
"blob:review-19",
"data:image/webp;base64,bGVnYWN5",
],
"labels": [
"Preview evidence 1 of 2: redacted.png",
"Preview evidence 2 of 2: legacy.webp",
],
"selected": ["false", "true"],
"preview": {
"hidden": False,
"src": "data:image/webp;base64,bGVnYWN5",
"alt": "Evidence 2 of 2: legacy.webp",
"position": "Evidence 2 of 2",
"filename": "legacy.webp",
"note": "Expected result",
},
"emptyHidden": True,
}
def test_failed_filing_keeps_preview_and_success_releases_blob_url_once():
script = f"""
const createReview = require({json.dumps(str(MODULE))});
function target() {{
const listeners={{}};
return {{hidden:true,disabled:false,textContent:'',children:[],src:'',alt:'',
addEventListener:(name,fn)=>listeners[name]=fn,
dispatch:(name,event={{}})=>listeners[name]?.({{preventDefault(){{}},...event}}),
replaceChildren(...items){{this.children=items;}},appendChild(item){{this.children.push(item);}},
setAttribute(){{}},focus(){{this.focused=true;}}}};
}}
const documentRef={{createElement:()=>target(),addEventListener:()=>{{}}}};
const sheet=target(), confirm=target(), trigger=target(), image=target(), preview=target();
const revoked=[];
let attempts=0;
const review=createReview({{
sheet,confirmButton:confirm,backButton:target(),evidenceList:target(),evidencePreview:preview,
evidenceImage:image,evidencePosition:target(),evidenceFilename:target(),evidenceNote:target(),evidenceEmpty:target(),
repository:target(),intent:target(),title:target(),body:target(),metadata:target(),status:target(),
document:documentRef,createObjectURL:blob=>'blob:attempt-'+blob.size,
revokeObjectURL:url=>revoked.push(url),
onConfirm:async()=>{{attempts+=1;if(attempts===1)throw new Error('Network unavailable');}},
}});
review.open({{draft:{{repository:'o/r',title:'Verify',attachment:{{filename:'one.png',blob:new Blob(['one'])}}}},intent:'create-and-assign'}},trigger);
confirm.dispatch('click');
setImmediate(()=>{{
const afterFailure={{hidden:sheet.hidden,previewHidden:preview.hidden,src:image.src,disabled:confirm.disabled,revoked:[...revoked]}};
confirm.dispatch('click');
setImmediate(()=>process.stdout.write(JSON.stringify({{
attempts,afterFailure,afterSuccess:{{hidden:sheet.hidden,triggerFocused:trigger.focused||false,revoked}}
}})));
}});
"""
assert run_node(script) == {
"attempts": 2,
"afterFailure": {
"hidden": False,
"previewHidden": False,
"src": "blob:attempt-3",
"disabled": False,
"revoked": [],
},
"afterSuccess": {
"hidden": True,
"triggerFocused": True,
"revoked": ["blob:attempt-3"],
},
}
def test_create_actions_review_the_complete_payload_before_durable_admission():
root = Path(__file__).parents[1]
html = (root / "frontend" / "index.html").read_text()
@@ -112,6 +228,12 @@ def test_create_actions_review_the_complete_payload_before_durable_admission():
assert 'id="issue-filing-review-title"' in html
assert 'id="issue-filing-review-body"' in html
assert 'id="issue-filing-review-evidence"' in html
assert 'id="issue-filing-review-evidence-empty"' in html
assert 'id="issue-filing-review-evidence-preview"' in html
assert 'id="issue-filing-review-evidence-image"' in html
assert 'id="issue-filing-review-evidence-position"' in html
assert 'id="issue-filing-review-evidence-filename"' in html
assert 'id="issue-filing-review-evidence-note"' in html
assert 'id="back-to-issue-edit"' in html
assert 'id="confirm-issue-filing"' in html
assert '<script src="static/issue-filing-review.js"></script>' in html
@@ -122,6 +244,10 @@ def test_create_actions_review_the_complete_payload_before_durable_admission():
assert "filingReview.open" in submit_handler
assert "issueOutbox.enqueueDurably" not in submit_handler
assert "async function admitReviewedIssue" in dashboard
assert "evidencePreview: qs('#issue-filing-review-evidence-preview')" in dashboard
assert "evidenceImage: qs('#issue-filing-review-evidence-image')" in dashboard
assert "createObjectURL: blob => URL.createObjectURL(blob)" in dashboard
assert "revokeObjectURL: url => URL.revokeObjectURL(url)" in dashboard
assert "issueOutbox.enqueueDurably" in dashboard.split(
"async function admitReviewedIssue", 1
)[1].split("qs('#create-issue-form').addEventListener", 1)[0]
@@ -130,6 +256,9 @@ def test_create_actions_review_the_complete_payload_before_durable_admission():
assert "max-height:100dvh" in css
assert "overflow-x:hidden" in css
assert "env(safe-area-inset-bottom)" in css
assert ".issue-filing-evidence-preview" in css
assert ".issue-filing-evidence-selector" in css
assert "min-height:44px" in css.split(".issue-filing-evidence-selector", 1)[1]
assert '"static/issue-filing-review.js"' in bundle