feat: capture shared mobile content (#223)
CI / lint (pull_request) Successful in 19s
CI / build-frontend (pull_request) Successful in 5s

This commit is contained in:
timmy committed 2026-08-07 20:22:21 +00:00
1 parent bca27f0330
commit f655a5115a
9 files changed
+313 -3

No files matched your search

+113
View File
@@ -534,6 +534,20 @@ async def test_new_issue_sheet_exposes_touch_safe_release_planning_controls():
assert "dueDate: qs('#create-issue-due-date').value" in html
@pytest.mark.anyio
async def test_dashboard_launches_share_capture_with_draft_conflict_choices():
html = await dashboard()
assert '<link rel="manifest" href="manifest.webmanifest"' in html
assert "issueCapture.stageSharedContent(sharedLaunch)" in html
assert 'id="use-shared-content"' in html
assert 'id="resume-issue-draft"' in html
assert 'id="shared-content-conflict"' in html
assert '.shared-content-actions button { min-height:44px;' in html
assert "history.replaceState({}, '', cleanUrl)" in html
assert "navigator.serviceWorker.register('/service-worker.js')" in html
@pytest.mark.anyio
async def test_mobile_my_work_exposes_touch_safe_milestone_lane_and_issue_editor():
html = await dashboard()
@@ -1232,6 +1246,105 @@ first.submit(draft).catch(() => {{
assert output["number"] == 17
def test_issue_capture_normalizes_shared_mobile_content_without_repeating_source_url():
script = f"""
const createIssueCapture = require({json.dumps(str(CREATE_ISSUE_SHEET))});
const normalized = createIssueCapture.normalizeSharedContent({{
title: ' Production alert ',
text: 'Latency crossed the threshold https://status.example/incidents/42',
url: 'https://status.example/incidents/42',
}});
process.stdout.write(JSON.stringify(normalized));
"""
result = subprocess.run(
["node", "-e", script], check=True, capture_output=True, text=True
)
assert json.loads(result.stdout) == {
"title": "Production alert",
"body": "Latency crossed the threshold https://status.example/incidents/42",
}
def test_issue_capture_suggests_a_title_when_mobile_share_only_contains_text():
script = f"""
const createIssueCapture = require({json.dumps(str(CREATE_ISSUE_SHEET))});
process.stdout.write(JSON.stringify(createIssueCapture.normalizeSharedContent({{
text: 'Investigate checkout latency before the release window opens. More diagnostic context follows.'
}})));
"""
result = subprocess.run(
["node", "-e", script], check=True, capture_output=True, text=True
)
assert json.loads(result.stdout) == {
"title": "Investigate checkout latency before the release window opens.",
"body": "Investigate checkout latency before the release window opens. More diagnostic context follows.",
}
def test_issue_capture_keeps_existing_draft_until_shared_content_is_accepted():
script = f"""
const createIssueCapture = require({json.dumps(str(CREATE_ISSUE_SHEET))});
const values = new Map();
const storage = {{
getItem:key => values.get(key) || null,
setItem:(key,value) => values.set(key,value),
removeItem:key => values.delete(key),
}};
let capture = createIssueCapture({{storage, fetchJson:()=>Promise.resolve({{}})}});
capture.saveDraft({{
repository:'stackchain/api', title:'Existing draft', body:'Keep me', labelIds:[3], milestoneId:9,
}});
const staged = capture.stageSharedContent({{title:'Shared alert', text:'Investigate', url:'https://status.example/42'}});
const before = capture.loadDraft();
capture = createIssueCapture({{storage, fetchJson:()=>Promise.resolve({{}})}});
const pendingAfterReload = capture.pendingSharedContent();
const accepted = capture.acceptSharedContent();
process.stdout.write(JSON.stringify({{staged, before, pendingAfterReload, accepted, pending:capture.pendingSharedContent()}}));
"""
result = subprocess.run(
["node", "-e", script], check=True, capture_output=True, text=True
)
assert json.loads(result.stdout) == {
"staged": {"status": "conflict"},
"before": {
"repository": "stackchain/api", "title": "Existing draft", "body": "Keep me",
"labelIds": [3], "milestoneId": 9,
},
"pendingAfterReload": {
"title": "Shared alert", "body": "Investigate\n\nhttps://status.example/42",
},
"accepted": {
"repository": "stackchain/api", "title": "Shared alert",
"body": "Investigate\n\nhttps://status.example/42", "labelIds": [3], "milestoneId": 9,
},
"pending": None,
}
def test_issue_capture_can_resume_existing_draft_and_discard_staged_share():
script = f"""
const createIssueCapture = require({json.dumps(str(CREATE_ISSUE_SHEET))});
const values = new Map();
const storage = {{getItem:key=>values.get(key)||null, setItem:(key,value)=>values.set(key,value), removeItem:key=>values.delete(key)}};
const capture = createIssueCapture({{storage, fetchJson:()=>Promise.resolve({{}})}});
capture.saveDraft({{repository:'stackchain/api', title:'Existing', body:'Keep', labelIds:[]}});
capture.stageSharedContent({{title:'Incoming', text:'Replace'}});
capture.discardSharedContent();
process.stdout.write(JSON.stringify({{draft:capture.loadDraft(), pending:capture.pendingSharedContent()}}));
"""
result = subprocess.run(
["node", "-e", script], check=True, capture_output=True, text=True
)
assert json.loads(result.stdout) == {
"draft": {"repository": "stackchain/api", "title": "Existing", "body": "Keep", "labelIds": []},
"pending": None,
}
def test_issue_capture_loads_repository_labels_with_priorities_first():
script = f"""
const createIssueCapture = require({json.dumps(str(CREATE_ISSUE_SHEET))});
+21 -1
View File
@@ -61,4 +61,24 @@ async def test_work_page_endpoint_rejects_unknown_stream_before_upstream_io(monk
response = await client.get("/api/v1/work/unknown?page=2")
assert response.status_code == 422
assert called is False
assert called is False
@pytest.mark.anyio
async def test_pwa_assets_expose_root_scoped_share_target_without_caching_api_data():
transport = httpx.ASGITransport(app=main.app)
async with httpx.AsyncClient(transport=transport, base_url="http://test") as client:
manifest = await client.get("/manifest.webmanifest")
worker = await client.get("/service-worker.js")
assert manifest.status_code == 200
assert manifest.headers["content-type"].startswith("application/manifest+json")
assert manifest.json()["share_target"] == {
"action": "/", "method": "GET", "enctype": "application/x-www-form-urlencoded",
"params": {"title": "title", "text": "text", "url": "url"},
}
assert worker.status_code == 200
assert worker.headers["content-type"].startswith("application/javascript")
assert worker.headers["service-worker-allowed"] == "/"
assert "request.url.includes('/api/')" in worker.text
assert "request.method !== 'GET'" in worker.text