stackchain-dashboard/tests/test_photo_draft_inbox.py
timmy fa876e1e38
Some checks failed
CI / lint (pull_request) Successful in 3m40s
CI / build-release (pull_request) Successful in 7s
CI / browser-journey (pull_request) Failing after 6m31s
CI / release-candidate (pull_request) Has been skipped
feat: surface photo-only reply drafts in My Work (Closes #1396)
2026-08-25 13:17:23 +00:00

111 lines
4.9 KiB
Python

import json
import subprocess
from pathlib import Path
MODULE = Path(__file__).parents[1] / "frontend" / "photo-draft-inbox.js"
INDEX = Path(__file__).parents[1] / "frontend" / "index.html"
DASHBOARD = Path(__file__).parents[1] / "frontend" / "dashboard.js"
WORKER = Path(__file__).parents[1] / "frontend" / "service-worker.js"
BUNDLE = Path(__file__).parents[1] / "src" / "frontend_bundle.py"
def run_node(script: str):
completed = subprocess.run(["node", "-e", script], check=True, capture_output=True, text=True)
return json.loads(completed.stdout)
def test_photo_draft_inbox_combines_stores_without_duplicate_target_cards_and_discards_exact_bundle():
script = f"""
const createPhotoDraftInbox = require({json.dumps(str(MODULE))});
const removed=[];
const conversation={{
list:async()=>[
{{id:'conversation-issue',kind:'photo-reply',photo_store:'conversation',title:'o/r#7',updated_at:30,route:{{kind:'issue',repository:'o/r',number:7}}}},
{{id:'conversation-update',kind:'photo-reply',photo_store:'conversation',title:'Update #8',updated_at:20,route:{{kind:'update',notification_id:8}}}},
],
remove:async target=>removed.push(['conversation',target]),
}};
const search={{
list:async()=>[
{{id:'search-pull',kind:'photo-reply',photo_store:'search',title:'o/r#9',updated_at:40,route:{{kind:'search',target_kind:'pull',repository:'o/r',number:9}}}},
],
remove:async target=>removed.push(['search',target]),
}};
const inbox=createPhotoDraftInbox({{conversation,search}});
(async()=>{{
const drafts=await inbox.refresh([{{kind:'issue-comment',route:{{kind:'issue',repository:'o/r',number:7}}}}]);
const description=inbox.description({{kind:'photo-reply',photo_count:2}});
const searchTarget=inbox.searchTarget(drafts[0]);
await inbox.discard(drafts[0]);
process.stdout.write(JSON.stringify({{drafts,description,searchTarget,remaining:inbox.list(),removed}}));
}})().catch(error=>{{console.error(error);process.exit(1);}});
"""
assert run_node(script) == {
"drafts": [
{
"id": "search-pull",
"kind": "photo-reply",
"photo_store": "search",
"title": "o/r#9",
"updated_at": 40,
"route": {"kind": "search", "target_kind": "pull", "repository": "o/r", "number": 9},
},
{
"id": "conversation-update",
"kind": "photo-reply",
"photo_store": "conversation",
"title": "Update #8",
"updated_at": 20,
"route": {"kind": "update", "notification_id": 8},
},
],
"description": "2 saved photos",
"searchTarget": {"kind": "pull", "repository": "o/r", "number": 9, "title": "o/r#9"},
"remaining": [
{
"id": "conversation-update",
"kind": "photo-reply",
"photo_store": "conversation",
"title": "Update #8",
"updated_at": 20,
"route": {"kind": "update", "notification_id": 8},
}
],
"removed": [["search", {"kind": "pull", "repository": "o/r", "number": 9}]],
}
def test_photo_draft_inbox_keeps_available_store_when_other_inventory_fails():
script = f"""
const createPhotoDraftInbox = require({json.dumps(str(MODULE))});
const inbox=createPhotoDraftInbox({{
conversation:{{list:async()=>{{throw new Error('IndexedDB unavailable')}}}},
search:{{list:async()=>[{{id:'safe',route:{{kind:'search',target_kind:'issue',repository:'o/r',number:2}},updated_at:1}}]}},
}});
(async()=>process.stdout.write(JSON.stringify(await inbox.refresh([]))))().catch(error=>{{console.error(error);process.exit(1);}});
"""
assert run_node(script) == [
{"id": "safe", "route": {"kind": "search", "target_kind": "issue", "repository": "o/r", "number": 2}, "updated_at": 1}
]
def test_mobile_my_work_discovers_reopens_and_discards_photo_only_reply_drafts():
index = INDEX.read_text()
dashboard = DASHBOARD.read_text()
worker = WORKER.read_text()
bundle = BUNDLE.read_text()
assert '<script src="static/photo-draft-inbox.js"></script>' in index
assert "BASE + 'static/photo-draft-inbox.js'" in worker
assert '"static/photo-draft-inbox.js"' in bundle
assert "const photoDraftInbox = createPhotoDraftInbox({" in dashboard
assert "draftInbox.list().concat(photoDraftInbox.list(), unfiled)" in dashboard
assert "await photoDraftInbox.refresh(draftInbox.list())" in dashboard
assert "void refreshPhotoDraftInbox();" in dashboard
assert "onChange:() => { void refreshPhotoDraftInbox(); }" in dashboard
assert "await searchReplyDraftStore.save(target, attachments);\n void refreshPhotoDraftInbox();" in dashboard
assert "photoDraftInbox.description(item)" in dashboard
assert "photoDraftInbox.searchTarget(item)" in dashboard
assert "await photoDraftInbox.discard(item)" in dashboard