feat: review ready mobile drafts in one tap (Closes #861)
This commit is contained in:
parent
ff41eb1d57
commit
c1c4ff8e31
|
|
@ -282,7 +282,7 @@ textarea { resize: vertical; min-height: 120px; }
|
|||
.draft-card:focus-visible { outline:3px solid #60a5fa; outline-offset:3px; border-color:#93c5fd; }
|
||||
.draft-preview { color:var(--muted); overflow-wrap:anywhere; }
|
||||
.draft-actions { display:grid; grid-template-columns:repeat(auto-fit,minmax(120px,1fr)); gap:8px; }
|
||||
.draft-actions button { min-height:44px; width:100%; }
|
||||
.draft-actions button, .draft-filing-session-actions button, .agenda-replan-actions button { min-height:44px; width:100%; }
|
||||
.delivery-center { display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap; grid-column:1/-1; padding:12px; border:1px solid #31577f; border-radius:12px; background:#10233a; }
|
||||
.delivery-center h3, .delivery-center p { margin:0; }
|
||||
.delivery-center button { min-height:44px; flex:0 0 auto; }
|
||||
|
|
@ -290,7 +290,7 @@ textarea { resize: vertical; min-height: 120px; }
|
|||
.draft-section { display:grid; grid-template-columns:repeat(auto-fit,minmax(260px,1fr)); gap:10px; grid-column:1/-1; }
|
||||
.draft-section > h3 { grid-column:1/-1; margin:8px 0 0; }
|
||||
.create-issue-actions { display:grid; grid-template-columns:repeat(auto-fit,minmax(140px,1fr)); gap:8px; padding-bottom:env(safe-area-inset-bottom); }
|
||||
.create-issue-actions button { min-height:44px; max-width:100%; width:100%; }
|
||||
.create-issue-actions button { min-height:44px; width:100%; }
|
||||
.create-issue-actions #create-issue-status { grid-column:1/-1; }
|
||||
.create-issue-estimate { display:grid; gap:6px; padding:10px; border:1px solid #31577f; border-radius:10px; background:#101d31; }
|
||||
.create-issue-estimate > span:first-of-type { display:flex; align-items:center; gap:8px; }
|
||||
|
|
@ -298,13 +298,11 @@ textarea { resize: vertical; min-height: 120px; }
|
|||
.draft-filing-session { position:sticky; top:0; z-index:2; display:grid; gap:6px; margin-bottom:12px; padding:10px; padding-bottom:calc(10px + env(safe-area-inset-bottom)); border:1px solid #31577f; border-radius:12px; background:rgba(11,21,38,.98); }
|
||||
.draft-filing-session[hidden] { display:none; }
|
||||
.draft-filing-session-actions { display:grid; grid-template-columns:1fr 1fr; gap:8px; }
|
||||
.draft-filing-session-actions button { min-height:44px; width:100%; }
|
||||
.my-work-card { min-height: 44px; display:grid; gap:8px; padding:12px; border:1px solid #1f3a5f; border-radius:12px; background:#0f1d33; color:var(--text); }
|
||||
.agenda-replan { margin:10px 0; padding:12px; border:1px solid #7c4a1d; border-radius:12px; background:#24170d; }
|
||||
.agenda-replan-launch { display:flex; align-items:center; justify-content:space-between; gap:12px; }
|
||||
.agenda-replan-launch p { margin:4px 0 0; }
|
||||
.agenda-replan-actions { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:8px; margin-top:10px; }
|
||||
.agenda-replan-actions button { min-height:44px; width:100%; }
|
||||
.agenda-replan-actions label { grid-column:1/-1; }
|
||||
.agenda-replan-actions input { box-sizing:border-box; min-height:44px; width:100%; max-width:100%; }
|
||||
.protect-today { margin:10px 0; padding:12px; border:1px solid #2f6f9f; border-radius:12px; background:#0d2136; display:flex; align-items:center; justify-content:space-between; gap:12px; }
|
||||
|
|
|
|||
|
|
@ -536,9 +536,11 @@
|
|||
);
|
||||
const dFS = createDraftFilingSession({list:()=>unfiledCaptures.list().filter(item=>!item.quarantined)});
|
||||
dFS.attach(qs, {
|
||||
captures:unfiledCaptures, issueCapture, attachment:createIssueAttachmentController,
|
||||
captures:unfiledCaptures, capture:()=>issueCapture, attachment:createIssueAttachmentController,
|
||||
getLogin:()=>activeFlushLogin, setResumedId:id=>{ rUC = id; },
|
||||
openSheet:openCreateIssueSheet, setFilingMode:setIssueFilingMode,
|
||||
review:r=>loadIssueFilingMetadata(r.repository,r)
|
||||
.then(()=>issueCapture.findDuplicates(currentIssueCaptureDraft())).then(renderIssueDuplicates),
|
||||
});
|
||||
let rUC = '';
|
||||
let backgroundIssueSync = null;
|
||||
|
|
@ -2655,19 +2657,16 @@
|
|||
'<button class="draft-resume" data-draft-index="' + index + '" type="button">Open review</button>' +
|
||||
'<button class="draft-discard" data-draft-index="' + index + '" type="button">Discard queued review</button>' :
|
||||
'<button class="draft-resume" data-draft-index="' + index + '" type="button">' +
|
||||
(isUnfiled ? 'Choose repository' : 'Resume draft') + '</button>' +
|
||||
(isUnfiled ? item.action : 'Resume draft') + '</button>' +
|
||||
'<button class="draft-discard" data-draft-index="' + index + '" type="button">Discard draft</button>';
|
||||
const state = (isOutbox || isUnfiled) ?
|
||||
'<span class="pill">' + (item.quarantined ? 'Identity protected' :
|
||||
(isUnfiled ? 'Needs filing' : item.status === 'completion' ? 'Created · ready to start' :
|
||||
item.status === 'attention' ? 'Needs attention' : item.status === 'sending' ? 'Sending' :
|
||||
item.status === 'authorization' ? 'Awaiting authorization' : 'Queued for sync')) + '</span>' +
|
||||
const state = isOutbox ?
|
||||
'<span class="pill">' + draftInbox.deliveryLabel(item) + '</span>' +
|
||||
(item.ownership ? '<div class="small">' + escapeHtml(item.ownership) + '</div>' : '') : '';
|
||||
const attempt = item.last_attempt_error ? '<span class="delivery-attempt small">Last attempt ' +
|
||||
escapeHtml(fmt(item.last_attempt_at)) + ' · ' + escapeHtml(item.last_attempt_error) + '</span>' : '';
|
||||
const captureTarget = isUnfiled ? ' tabindex="-1" data-capture-id="' + escapeHtml(item.capture_id) + '"' : '';
|
||||
return '<article class="my-work-card draft-card"' + captureTarget + '>' +
|
||||
'<span class="small">' + escapeHtml(item.label) + (item.repository ? ' · ' + escapeHtml(item.repository) : '') + '</span>' +
|
||||
'<span class="small">' + escapeHtml([item.label, item.repository, item.details].filter(Boolean).join(' · ')) + '</span>' +
|
||||
'<span class="my-work-card-title">' + escapeHtml(item.title) + '</span>' +
|
||||
'<span class="draft-preview">' + escapeHtml(item.preview || 'Unfinished draft') + '</span>' + state + attempt +
|
||||
'<span class="small">Saved ' + escapeHtml(fmt(item.updated_at)) + '</span>' +
|
||||
|
|
@ -2705,8 +2704,7 @@
|
|||
if (!item) return;
|
||||
if (item.kind === 'unfiled-issue') {
|
||||
try {
|
||||
dFS.start(item.capture_id);
|
||||
await dFS.nextCapture();
|
||||
await dFS.open(item.capture_id, item.ready ? button : null);
|
||||
} catch (error) { qs('#my-work-action-status').textContent = error.message; }
|
||||
} else if (item.kind === 'new-issue') openCreateIssueSheet();
|
||||
else if (item.route) workRoute.open(item.route);
|
||||
|
|
@ -5326,7 +5324,8 @@
|
|||
todayCapacity: startCapacity,
|
||||
} : {}),
|
||||
};
|
||||
filingReview.open({draft: durableDraft, intent}, event.submitter || qs('#submit-new-issue'));
|
||||
filingReview.open({draft: durableDraft, intent},
|
||||
dFS.takeTrigger() || event.submitter || qs('#submit-new-issue'));
|
||||
qs('#create-issue-status').textContent = 'Review the complete payload, then confirm filing.';
|
||||
} catch (error) {
|
||||
qs('#create-issue-status').textContent = error.message + ' Your draft is safe; retry.';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
function createDraftFilingSession({list}) {
|
||||
let orderedIds = [];
|
||||
let currentId = '';
|
||||
let reviewTrigger = null;
|
||||
|
||||
function visibleIds() {
|
||||
const available = new Set(list().map(item => item.id));
|
||||
|
|
@ -49,18 +50,42 @@ function createDraftFilingSession({list}) {
|
|||
|
||||
const session = {start, current:state, next:()=>move(1), previous:()=>move(-1), removeCurrentAndNext};
|
||||
session.attach = (qs, dependencies) => {
|
||||
const openCapture = async captureId => {
|
||||
const openCapture = async (captureId, trigger = null) => {
|
||||
const resumed = await dependencies.captures.resume(captureId, dependencies.getLogin());
|
||||
dependencies.issueCapture.saveDraft(resumed);
|
||||
const issueCapture = dependencies.capture?.() || dependencies.issueCapture;
|
||||
issueCapture.saveDraft(resumed);
|
||||
dependencies.setResumedId(captureId);
|
||||
await dependencies.openSheet();
|
||||
await dependencies.openSheet(false);
|
||||
const evidence = resumed.attachments || resumed.attachment;
|
||||
dependencies.attachment[evidence ? 'restore' : 'clear'](evidence);
|
||||
dependencies.setFilingMode(true);
|
||||
session.render();
|
||||
if (trigger) {
|
||||
await dependencies.review(resumed);
|
||||
reviewTrigger = trigger;
|
||||
const submitter = resumed.completionIntent === 'create-and-start' ?
|
||||
qs('#create-and-start-issue') : qs('#submit-new-issue');
|
||||
const disabled = submitter.disabled;
|
||||
submitter.disabled = false;
|
||||
qs('#create-issue-form').requestSubmit(submitter);
|
||||
submitter.disabled = disabled;
|
||||
}
|
||||
};
|
||||
return Object.assign(session, {
|
||||
nextCapture() { const state = session.current(); return state ? openCapture(state.id) : null; },
|
||||
reviewCapture(captureId, trigger) {
|
||||
session.start(captureId);
|
||||
return openCapture(captureId, trigger);
|
||||
},
|
||||
open(captureId, trigger = null) {
|
||||
session.start(captureId);
|
||||
return openCapture(captureId, trigger);
|
||||
},
|
||||
takeTrigger() {
|
||||
const trigger = reviewTrigger;
|
||||
reviewTrigger = null;
|
||||
return trigger;
|
||||
},
|
||||
render() {
|
||||
const state = session.current();
|
||||
qs('#draft-filing-session').hidden = !state;
|
||||
|
|
|
|||
|
|
@ -251,7 +251,16 @@ function createDraftInbox({ storage, getCurrentLogin = () => '', now = () => Dat
|
|||
return { drafts, deliveries, counts, retryable };
|
||||
}
|
||||
|
||||
return { list, discard, partition };
|
||||
function deliveryLabel(item) {
|
||||
if (item.quarantined) return 'Identity protected';
|
||||
if (item.status === 'completion') return 'Created · ready to start';
|
||||
if (item.status === 'attention') return 'Needs attention';
|
||||
if (item.status === 'sending') return 'Sending';
|
||||
if (item.status === 'authorization') return 'Awaiting authorization';
|
||||
return 'Queued for sync';
|
||||
}
|
||||
|
||||
return { list, discard, partition, deliveryLabel };
|
||||
}
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) module.exports = createDraftInbox;
|
||||
|
|
|
|||
|
|
@ -4,8 +4,18 @@ function withFilingEstimate(draft, value) {
|
|||
}
|
||||
|
||||
function unfiledDraftSummary(item) {
|
||||
return {label:item.filingPlan?.repository ? 'Ready to file' : 'Needs filing',
|
||||
repository:item.filingPlan?.repository || ''};
|
||||
const plan = item.filingPlan || {};
|
||||
const ready = Boolean(plan.repository);
|
||||
const startsToday = plan.completionIntent === 'create-and-start';
|
||||
const evidenceCount = Number(item.attachmentCount || (item.hasAttachment ? 1 : 0));
|
||||
const evidence = evidenceCount ? `${evidenceCount} screenshot${evidenceCount === 1 ? '' : 's'}` : 'No screenshots';
|
||||
return {
|
||||
label:ready ? 'Ready to file' : 'Needs filing',
|
||||
repository:plan.repository || '', ready,
|
||||
action:ready ? (startsToday ? 'Review & start' : 'Review & file') : 'Choose repository',
|
||||
details:[startsToday ? 'Create & start' : 'Create issue',
|
||||
plan.estimateMinutes ? `${plan.estimateMinutes} min` : '', evidence].filter(Boolean).join(' · '),
|
||||
};
|
||||
}
|
||||
|
||||
function unfiledSavedMessage(item) {
|
||||
|
|
@ -409,4 +419,7 @@ function createUnfiledCaptures({
|
|||
subscribe:listener => (listeners.add(listener), () => listeners.delete(listener))};
|
||||
}
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) module.exports = createUnfiledCaptures;
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = createUnfiledCaptures;
|
||||
module.exports.summary = unfiledDraftSummary;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -270,13 +270,17 @@ const values = new Map([
|
|||
]);
|
||||
const storage = {{get length(){{return values.size}},key:i=>Array.from(values.keys())[i]||null,getItem:k=>values.get(k)||null,setItem:(k,v)=>values.set(k,v)}};
|
||||
const inbox = createDraftInbox({{storage,getCurrentLogin:()=>'timmy'}});
|
||||
process.stdout.write(JSON.stringify(inbox.partition()));
|
||||
const partition=inbox.partition();
|
||||
process.stdout.write(JSON.stringify({{...partition,labels:partition.deliveries.map(inbox.deliveryLabel)}}));
|
||||
"""
|
||||
output = run_node(script)
|
||||
|
||||
assert len(output["drafts"]) == 1
|
||||
assert len(output["deliveries"]) == 5
|
||||
assert output["counts"] == {"waiting": 2, "sending": 1, "attention": 2, "authorization": 0}
|
||||
assert output["labels"] == [
|
||||
"Needs attention", "Identity protected", "Needs attention", "Sending", "Queued for sync"
|
||||
]
|
||||
assert [item["outbox_id"] for item in output["retryable"]] == ["waiting"]
|
||||
waiting = next(item for item in output["deliveries"] if item["outbox_id"] == "waiting")
|
||||
assert waiting["last_attempt_at"] == 90
|
||||
|
|
@ -293,7 +297,7 @@ async def test_mobile_dashboard_exposes_touch_safe_draft_recovery_lane():
|
|||
assert 'class="draft-resume"' in html
|
||||
assert 'class="draft-discard"' in html
|
||||
assert 'Discard this unfinished draft?' in html
|
||||
assert '.draft-actions button { min-height:44px;' in html
|
||||
assert '.draft-actions button, .draft-filing-session-actions button, .agenda-replan-actions button { min-height:44px;' in html
|
||||
assert 'createDraftInbox({ storage: localStorage' in html
|
||||
assert "captureDraft.repository && !issueCaptureRepositories.includes(captureDraft.repository)" in html
|
||||
assert "Verified not posted — retry" in html
|
||||
|
|
@ -316,7 +320,8 @@ async def test_mobile_dashboard_renders_delivery_center_separately_and_retries_w
|
|||
assert "const deliveryCenter = draftInbox.partition(displayedDrafts);" in html
|
||||
assert "await Promise.all([issueOutbox.flush(activeFlushLogin), authoredOutbox.flush(activeFlushLogin)])" in html
|
||||
assert "deliveryCenter.retryable.length" in html
|
||||
assert "item.status === 'sending' ? 'Sending'" in html
|
||||
assert "draftInbox.deliveryLabel(item)" in html
|
||||
assert "if (item.status === 'sending') return 'Sending'" in DRAFTS.read_text()
|
||||
assert "item.last_attempt_error ? '<span class=\"delivery-attempt small\">Last attempt '" in html
|
||||
assert '.delivery-center { display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap;' in html
|
||||
assert '.delivery-center button { min-height:44px;' in html
|
||||
|
|
@ -330,7 +335,7 @@ async def test_mobile_dashboard_requires_an_explicit_authorize_and_close_gesture
|
|||
assert '>Authorize & close</button>' in html
|
||||
assert "list.querySelectorAll('.draft-authorize')" in html
|
||||
assert "authoredOutbox.retry(item.outbox_id, activeFlushLogin)" in html
|
||||
assert "item.status === 'authorization' ? 'Awaiting authorization'" in html
|
||||
assert "if (item.status === 'authorization') return 'Awaiting authorization'" in DRAFTS.read_text()
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
|
|||
|
|
@ -4246,7 +4246,7 @@ async def test_every_mobile_repository_selection_uses_the_resilient_metadata_loa
|
|||
assert "renderLabels(repository, metadata.labels" in source
|
||||
assert "renderMilestones(repository, metadata.milestones" in source
|
||||
assert "templatePicker.render(repository, metadata.templates" in source
|
||||
assert html.count("loadIssueFilingMetadata(") == 4
|
||||
assert html.count("loadIssueFilingMetadata(") == 5
|
||||
assert "loadIssueLabels(repository);\n loadIssueMilestones(repository);" not in html
|
||||
assert "loadIssueLabels(event.target.value).then" not in html
|
||||
assert "loadIssueLabels(qs('#create-issue-repository').value" not in html
|
||||
|
|
|
|||
|
|
@ -60,6 +60,78 @@ session.start('planned');
|
|||
assert run_node(script) == [[{"filename": "one.png"}, {"filename": "two.png"}]]
|
||||
|
||||
|
||||
def test_ready_draft_summary_exposes_truthful_mobile_action_and_plan_details():
|
||||
script = f"""
|
||||
const createUnfiledCaptures=require({json.dumps(str(UNFILED))});
|
||||
const planned=createUnfiledCaptures.summary({{
|
||||
filingPlan:{{repository:'stackchain/dashboard',completionIntent:'create-and-start',estimateMinutes:45}},
|
||||
attachmentCount:2,
|
||||
}});
|
||||
const minimal=createUnfiledCaptures.summary({{}});
|
||||
process.stdout.write(JSON.stringify({{planned,minimal}}));
|
||||
"""
|
||||
|
||||
assert run_node(script) == {
|
||||
"planned": {
|
||||
"label": "Ready to file",
|
||||
"repository": "stackchain/dashboard",
|
||||
"ready": True,
|
||||
"action": "Review & start",
|
||||
"details": "Create & start · 45 min · 2 screenshots",
|
||||
},
|
||||
"minimal": {
|
||||
"label": "Needs filing",
|
||||
"repository": "",
|
||||
"ready": False,
|
||||
"action": "Choose repository",
|
||||
"details": "Create issue · No screenshots",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_ready_draft_opens_final_review_with_restored_intent_and_evidence():
|
||||
script = f"""
|
||||
const createDraftFilingSession=require({json.dumps(str(DRAFT_SESSION))});
|
||||
const events=[];
|
||||
const trigger={{id:'ready-action'}};
|
||||
const resumed={{repository:'o/r',title:'Planned',body:'Evidence',labelIds:[7],
|
||||
completionIntent:'create-and-start',estimateMinutes:30,
|
||||
attachments:[{{filename:'one.png'}},{{filename:'two.png'}}]}};
|
||||
const elements=new Map();
|
||||
const qs=selector=>{{
|
||||
if(!elements.has(selector)) elements.set(selector,{{id:selector.slice(1),hidden:false,disabled:false,textContent:'',addEventListener:()=>{{}},
|
||||
requestSubmit:button=>events.push(['review',resumed,button.id])}});
|
||||
return elements.get(selector);
|
||||
}};
|
||||
const session=createDraftFilingSession({{list:()=>[{{id:'planned'}}]}});
|
||||
session.attach(qs,{{
|
||||
captures:{{resume:async()=>resumed}},
|
||||
capture:()=>({{saveDraft:value=>events.push(['save',value])}}),
|
||||
attachment:{{restore:value=>events.push(['evidence',value]),clear:()=>{{}}}},
|
||||
getLogin:()=>'timmy',setResumedId:id=>events.push(['source',id]),
|
||||
openSheet:async()=>events.push(['sheet']),setFilingMode:()=>events.push(['filing']),
|
||||
review:async()=>{{events.push(['metadata']);events.push(['duplicates']);}},
|
||||
}});
|
||||
session.start('planned');
|
||||
(async()=>{{await session.reviewCapture('planned',trigger);process.stdout.write(JSON.stringify({{
|
||||
events,origin:session.takeTrigger().id
|
||||
}}));}})();
|
||||
"""
|
||||
|
||||
output = run_node(script)
|
||||
events = output["events"]
|
||||
assert [event[0] for event in events] == [
|
||||
"save", "source", "sheet", "evidence", "filing", "metadata",
|
||||
"duplicates", "review"
|
||||
]
|
||||
assert events[-1][1]["labelIds"] == [7]
|
||||
assert events[-1][1]["attachments"] == [
|
||||
{"filename": "one.png"}, {"filename": "two.png"}
|
||||
]
|
||||
assert events[-1][2] == "create-and-start-issue"
|
||||
assert output["origin"] == "ready-action"
|
||||
|
||||
|
||||
def test_unfiled_captures_block_at_capacity_until_oldest_is_explicitly_replaced():
|
||||
script = f"""
|
||||
const createUnfiledCaptures = require({json.dumps(str(UNFILED))});
|
||||
|
|
@ -599,7 +671,7 @@ async def test_mobile_composer_exposes_cold_offline_save_and_account_safe_resume
|
|||
assert "await unfiledCaptures.completeResume(rUC)" in html
|
||||
assert "...(rUC ? { sourceCaptureId: rUC } : {})" in html
|
||||
assert "item.hasAttachment ? ' · Screenshot attached' : ''" in html
|
||||
assert "dependencies.issueCapture.saveDraft(resumed)" in DRAFT_SESSION.read_text()
|
||||
assert "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
|
||||
|
|
@ -618,11 +690,28 @@ async def test_mobile_planned_draft_can_be_saved_and_resumed_without_replanning(
|
|||
assert "qs('#create-issue-estimate').value = captureDraft.estimateMinutes || '';" in html
|
||||
assert "qs('#file-new-issue').hidden = enabled" in feature
|
||||
assert "qs('.create-issue-capture-actions').hidden = false" in feature
|
||||
assert "label:item.filingPlan?.repository ? 'Ready to file' : 'Needs filing'" in feature
|
||||
assert "repository:item.filingPlan?.repository || ''" in feature
|
||||
assert "label:ready ? 'Ready to file' : 'Needs filing'" in feature
|
||||
assert "repository:plan.repository || ''" in feature
|
||||
assert "Saved planned Draft" in feature
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_mobile_ready_draft_card_opens_validated_final_review_in_one_tap():
|
||||
html = await dashboard()
|
||||
|
||||
assert "isUnfiled ? item.action" in html
|
||||
assert "[item.label, item.repository, item.details].filter(Boolean).join(' · ')" in html
|
||||
assert "escapeHtml([item.label" in html
|
||||
assert "await dFS.open(item.capture_id, item.ready ? button : null)" in html
|
||||
assert "capture:()=>issueCapture" in html
|
||||
assert "review:r=>loadIssueFilingMetadata(r.repository,r)" in html
|
||||
assert ".then(()=>issueCapture.findDuplicates(currentIssueCaptureDraft())).then(renderIssueDuplicates)" in html
|
||||
assert "qs('#create-issue-form').requestSubmit(submitter)" in DRAFT_SESSION.read_text()
|
||||
assert "dFS.takeTrigger() || event.submitter || qs('#submit-new-issue')" in html
|
||||
assert '.draft-actions button, .draft-filing-session-actions button, .agenda-replan-actions button { min-height:44px; width:100%; }' in html
|
||||
assert '@media(max-width:320px)' in html
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_mobile_drafts_expose_a_safe_sequential_filing_session():
|
||||
html = await dashboard()
|
||||
|
|
@ -632,13 +721,12 @@ async def test_mobile_drafts_expose_a_safe_sequential_filing_session():
|
|||
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.open(item.capture_id, item.ready ? button : null)" 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 '.draft-actions button, .draft-filing-session-actions button, .agenda-replan-actions button { min-height:44px;' in html
|
||||
assert 'padding-bottom:calc(10px + env(safe-area-inset-bottom))' in html
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user