feat: file mobile Drafts sequentially (Closes #629)
CI / lint (pull_request) Successful in 1m29s
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-12 07:18:02 +00:00
1 parent 1079ec6561
commit 7948d2e353
8 files changed
+176 -18

No files matched your search

+1
View File
@@ -684,6 +684,7 @@ def test_install_precaches_complete_subpath_scoped_app_shell():
"/dashboard/static/widgets.js",
"/dashboard/static/drafts.js",
"/dashboard/static/unfiled-captures.js",
"/dashboard/static/draft-filing-session.js",
"/dashboard/static/draft-capacity-dialog.js",
"/dashboard/static/outbox-coordinator.js",
"/dashboard/static/issue-outbox.js",
+46 -4
View File
@@ -8,6 +8,7 @@ from tests.dashboard_bundle import dashboard
UNFILED = Path(__file__).parents[1] / "frontend" / "unfiled-captures.js"
DRAFT_SESSION = Path(__file__).parents[1] / "frontend" / "draft-filing-session.js"
def run_node(script: str):
@@ -15,6 +16,28 @@ def run_node(script: str):
return json.loads(result.stdout)
def test_draft_filing_session_navigates_stable_capture_ids_and_reconciles_removed_items():
script = f"""
const createDraftFilingSession = require({json.dumps(str(DRAFT_SESSION))});
let captures = [{{id:'newest'}}, {{id:'middle'}}, {{id:'oldest'}}];
const session = createDraftFilingSession({{list:()=>captures}});
const opened = session.start('middle');
const skipped = session.next();
captures = [{{id:'newest'}}];
const afterFiled = session.removeCurrentAndNext('oldest');
const previous = session.previous();
process.stdout.write(JSON.stringify({{opened,skipped,afterFiled,previous}}));
"""
output = run_node(script)
assert output == {
"opened": {"id": "middle", "position": 2, "total": 3, "remaining": 1},
"skipped": {"id": "oldest", "position": 3, "total": 3, "remaining": 0},
"afterFiled": {"id": "newest", "position": 1, "total": 1, "remaining": 0},
"previous": {"id": "newest", "position": 1, "total": 1, "remaining": 0},
}
def test_unfiled_captures_block_at_capacity_until_oldest_is_explicitly_replaced():
script = f"""
const createUnfiledCaptures = require({json.dumps(str(UNFILED))});
@@ -335,13 +358,13 @@ async def test_mobile_composer_exposes_cold_offline_save_and_account_safe_resume
assert "requestAnimationFrame(() =>" in html
assert "savedCard?.scrollIntoView({block:'nearest'})" in html
assert "Saved to Drafts. Choose a repository when you’re ready to file it." in html
assert "await unfiledCaptures.resume(item.capture_id, activeFlushLogin)" in html
assert "await dependencies.captures.resume(captureId, dependencies.getLogin())" in DRAFT_SESSION.read_text()
assert "await createIssueAttachmentController.serialize()" in html
assert "createUnfiledAttachmentStore()" in html
assert "createIssueAttachmentController.restore(resumed.attachment)" in html
assert "await unfiledCaptures.completeResume(resumedUnfiledCaptureId)" in html
assert "dependencies.attachment[resumed.attachment ? 'restore' : 'clear'](resumed.attachment)" in DRAFT_SESSION.read_text()
assert "await unfiledCaptures.completeResume(rUC)" in html
assert "item.hasAttachment ? ' · Screenshot attached' : ''" in html
assert "issueCapture.saveDraft(resumed)" in html
assert "dependencies.issueCapture.saveDraft(resumed)" in DRAFT_SESSION.read_text()
assert "item.kind === 'unfiled-issue'" in html
assert '.create-issue-actions button { min-height:44px;' in html
assert '.draft-card:focus-visible' in html
@@ -349,6 +372,25 @@ async def test_mobile_composer_exposes_cold_offline_save_and_account_safe_resume
assert '@media(max-width:320px)' in html
@pytest.mark.anyio
async def test_mobile_drafts_expose_a_safe_sequential_filing_session():
html = await dashboard()
assert '<script src="static/draft-filing-session.js"></script>' in html
assert 'id="draft-filing-session"' in html
assert 'id="draft-filing-progress"' in html
assert 'id="previous-draft"' in html
assert 'id="skip-draft"' in html
assert "dFS.start(item.capture_id)" in html
assert "await dFS.nextCapture()" in html
assert "await dFS.advance(fS.id)" in html
feature = DRAFT_SESSION.read_text()
assert "Draft ${state.position} of ${state.total}" in feature
assert "qs('#submit-new-issue').textContent = state.remaining ? 'File & next' : 'File final Draft'" in feature
assert '.draft-filing-session-actions button { min-height:44px;' in html
assert 'padding-bottom:calc(10px + env(safe-area-inset-bottom))' in html
@pytest.mark.anyio
async def test_mobile_capture_capacity_requires_an_explicit_preserving_decision():
html = await dashboard()