1 parent
718f0d1209
commit
5ec4dd7f6e
19 files changed
+224
-63
No files matched your search
@@ -303,4 +303,4 @@ async def test_unread_update_offers_reply_mark_read_and_next_independent_of_toda
|
||||
assert '.update-reply-actions { display:grid; grid-template-columns:repeat(2,minmax(0,1fr));' in html
|
||||
assert '.update-reply-actions button { min-height:44px;' in html
|
||||
worker = (Path(__file__).parents[1] / "frontend" / "service-worker.js").read_text()
|
||||
assert "stackchain-dashboard-shell-v140" in worker
|
||||
assert "stackchain-dashboard-shell-v141" in worker
|
||||
@@ -593,7 +593,7 @@ process.stdout.write(JSON.stringify({{
|
||||
assert ".following-disposition-mode" in css
|
||||
assert "if (searchPreviewReturnKind === 'following')" in dashboard
|
||||
assert "e.key === 'Escape' && searchPreviewReturnKind === 'following'" in dashboard
|
||||
assert "stackchain-dashboard-shell-v140" in service_worker
|
||||
assert "stackchain-dashboard-shell-v141" in service_worker
|
||||
|
||||
|
||||
def test_prepare_today_lazily_refreshes_and_directly_reviews_following():
|
||||
|
||||
@@ -4,6 +4,7 @@ from pathlib import Path
|
||||
|
||||
|
||||
MODULE = Path(__file__).parents[1] / "frontend" / "human-gates.js"
|
||||
PROGRESSIVE = Path(__file__).parents[1] / "frontend" / "progressive-human-gates.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"
|
||||
@@ -195,10 +196,57 @@ def test_human_gate_mobile_shell_and_deep_route_are_wired():
|
||||
assert 'data-mobile-queue="gate"' in index
|
||||
assert 'data-mobile-queue-count="gate"' in index
|
||||
assert "openHumanGates: () => openHumanGates()" in dashboard
|
||||
assert "onChange:(snapshot, state)=>" in dashboard
|
||||
assert "const humanGatesOnChange = (snapshot, state)=>" in dashboard
|
||||
assert "queueCounts.gate = snapshot.pending_count" in dashboard
|
||||
assert "preparationItems.gate = snapshot.items" in dashboard
|
||||
assert "mobileStartDay.reconcile({authoritative:true, authoritativePhases:['gate']})" in dashboard
|
||||
assert "counts.gate = queueCounts.gate" in dashboard
|
||||
assert "gate:preparationItems.gate || []" in dashboard
|
||||
assert "stackchain-dashboard-shell-v140" in WORKER.read_text()
|
||||
assert "stackchain-dashboard-shell-v141" in WORKER.read_text()
|
||||
|
||||
|
||||
def test_deep_link_opens_human_gates_without_waiting_for_optional_workspace():
|
||||
script = f"""
|
||||
const createProgressiveHumanGates=require({json.dumps(str(PROGRESSIVE))});
|
||||
const listeners={{}}; const requests=[];
|
||||
const nodes={{
|
||||
'#human-gates-count':{{}}, '#human-gates-list':{{innerHTML:''}},
|
||||
'#human-gates-status':{{}}, '#human-gates':{{hidden:true}},
|
||||
'#human-gate-detail':{{innerHTML:'',querySelectorAll:()=>[]}},
|
||||
'#open-human-gates':{{addEventListener:(name,fn)=>listeners.open=fn}},
|
||||
'#close-human-gates':{{addEventListener:(name,fn)=>listeners.close=fn}},
|
||||
}};
|
||||
nodes['#human-gates-list'].addEventListener=(name,fn)=>listeners.list=fn;
|
||||
nodes['#human-gate-detail'].addEventListener=(name,fn)=>listeners.detail=fn;
|
||||
const document={{querySelector:selector=>nodes[selector]||null}};
|
||||
const app=createProgressiveHumanGates({{
|
||||
document, location:{{hash:'#/my-work/human-gates'}},
|
||||
history:{{replaceState(){{}}}}, storage:{{getItem:()=>null,setItem(){{}}}},
|
||||
isOnline:()=>true, getIdentity:async()=>({{login:'timmy',accountKey:'7:timmy'}}),
|
||||
fetchJson:async path=>{{requests.push(path);return {{pending_count:1,items:[{{id:'g1',title:'Ship it',candidate_hash:'abc',revision:1,checks:[]}}]}};}},
|
||||
}});
|
||||
(async()=>{{const started=await app.start();process.stdout.write(JSON.stringify({{
|
||||
started,hidden:nodes['#human-gates'].hidden,html:nodes['#human-gates-list'].innerHTML,
|
||||
requests,listeners:Object.keys(listeners).sort(),handoff:app.handoff().started,
|
||||
}}));}})();
|
||||
"""
|
||||
result = subprocess.run(["node", "-e", script], check=True, text=True, capture_output=True)
|
||||
output = json.loads(result.stdout)
|
||||
assert output == {
|
||||
"started": True,
|
||||
"hidden": False,
|
||||
"html": '<button class="human-gate-card" type="button" data-human-gate-id="g1"><strong>Ship it</strong><code>abc</code><span>Priority 0</span></button>',
|
||||
"requests": ["api/v1/human-gates", "api/v1/human-gates/g1"],
|
||||
"listeners": ["close", "detail", "list", "open"],
|
||||
"handoff": True,
|
||||
}
|
||||
|
||||
|
||||
def test_dashboard_adopts_progressive_human_gates_without_a_second_list_load():
|
||||
dashboard = DASHBOARD.read_text()
|
||||
index = INDEX.read_text()
|
||||
assert 'static/progressive-human-gates.js' in index
|
||||
assert "window.stackchainProgressiveHumanGates?.handoff?.()" in dashboard
|
||||
assert "progressiveHumanGatesHandoff?.controller || createHumanGates" in dashboard
|
||||
assert "if (!progressiveHumanGatesHandoff?.started) humanGates.load()" in dashboard
|
||||
assert "if (window.location.hash === '#/my-work/human-gates' && !progressiveHumanGatesHandoff?.started) openHumanGates()" in dashboard
|
||||
@@ -435,5 +435,5 @@ async def test_dashboard_syncs_every_later_change_and_exposes_account_status():
|
||||
def test_later_sync_ships_atomically_in_the_offline_shell():
|
||||
source = (Path(__file__).parents[1] / "frontend" / "service-worker.js").read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v140" in source
|
||||
assert "stackchain-dashboard-shell-v141" in source
|
||||
assert "BASE + 'static/later-sync.js'" in source
|
||||
@@ -256,4 +256,4 @@ def test_markdown_work_bodies_are_mobile_safe_block_containers():
|
||||
assert ".markdown-content { min-width:0; max-width:100%; overflow-wrap:anywhere;" in css
|
||||
assert ".markdown-content pre { max-width:100%; overflow-x:auto;" in css
|
||||
assert ".markdown-content a { min-height:44px;" in css
|
||||
assert "stackchain-dashboard-shell-v140" in worker
|
||||
assert "stackchain-dashboard-shell-v141" in worker
|
||||
@@ -45,7 +45,7 @@ def test_offline_shell_contains_every_local_dashboard_runtime_asset():
|
||||
shell_assets = set(re.findall(r"BASE \+ '([^']+)'", worker.split("async function sessionCsrf", 1)[0]))
|
||||
|
||||
assert local_assets <= shell_assets, f"Offline shell is missing: {sorted(local_assets - shell_assets)}"
|
||||
assert "stackchain-dashboard-shell-v140" in worker
|
||||
assert "stackchain-dashboard-shell-v141" in worker
|
||||
|
||||
|
||||
def test_all_conversation_composers_offer_accessible_mobile_mentions():
|
||||
|
||||
@@ -383,7 +383,7 @@ def test_mobile_dashboard_mounts_phone_safe_device_setup_flow():
|
||||
assert "controller.recoverPermission('deadline')" in dashboard
|
||||
assert "pushControllerReady.then(ensureDeviceSetup)" in dashboard
|
||||
assert "BASE + 'static/mobile-device-setup.js'" in worker
|
||||
assert "stackchain-dashboard-shell-v140" in worker
|
||||
assert "stackchain-dashboard-shell-v141" in worker
|
||||
assert ".device-setup-panel" in css
|
||||
assert ".device-readiness-card" in css
|
||||
assert "overflow-x:hidden" in css
|
||||
|
||||
@@ -274,5 +274,5 @@ async def test_mobile_home_progressively_discloses_secondary_panels_as_insights(
|
||||
def test_mobile_insights_rolls_into_the_offline_shell():
|
||||
worker = (CONTROLLER.parent / "service-worker.js").read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v140" in worker
|
||||
assert "stackchain-dashboard-shell-v141" in worker
|
||||
assert "BASE + 'static/mobile-insights.js'" in worker
|
||||
@@ -469,7 +469,7 @@ async def test_dashboard_wires_thumb_safe_start_day_briefing_into_offline_mobile
|
||||
assert ".mobile-start-day-finish { min-height:44px;" in html
|
||||
assert "max-width:100%; overflow-wrap:anywhere;" in html
|
||||
assert "BASE + 'static/mobile-start-day.js'" in service_worker
|
||||
assert "stackchain-dashboard-shell-v140" in service_worker
|
||||
assert "stackchain-dashboard-shell-v141" in service_worker
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
||||
@@ -418,7 +418,7 @@ async def test_starting_saved_today_work_closes_a_concurrent_rollover_planner():
|
||||
def test_plan_today_controller_is_available_in_the_offline_shell():
|
||||
source = SERVICE_WORKER.read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v140" in source
|
||||
assert "stackchain-dashboard-shell-v141" in source
|
||||
assert "BASE + 'static/plan-today.js'" in source
|
||||
assert "BASE + 'static/plan-today-readiness.js'" in source
|
||||
assert "BASE + 'static/plan-today-preview.js'" in source
|
||||
@@ -189,7 +189,7 @@ async function dispatchPush(payload) {{
|
||||
def test_week_unplan_undo_rolls_the_offline_shell():
|
||||
source = WORKER.read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v140" in source
|
||||
assert "stackchain-dashboard-shell-v141" in source
|
||||
assert "BASE + 'static/week-plan.js'" in source
|
||||
assert "BASE + 'static/dashboard.css'" in source
|
||||
|
||||
@@ -197,20 +197,20 @@ def test_week_unplan_undo_rolls_the_offline_shell():
|
||||
def test_private_today_action_mailbox_rolls_the_offline_shell():
|
||||
source = WORKER.read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v140" in source
|
||||
assert "stackchain-dashboard-shell-v141" in source
|
||||
|
||||
|
||||
def test_per_day_week_conflict_ui_rolls_the_offline_shell():
|
||||
source = WORKER.read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v140" in source
|
||||
assert "stackchain-dashboard-shell-v141" in source
|
||||
assert "BASE + 'static/week-plan.js'" in source
|
||||
|
||||
|
||||
def test_resumable_today_session_ships_in_a_new_offline_shell():
|
||||
source = WORKER.read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v140" in source
|
||||
assert "stackchain-dashboard-shell-v141" in source
|
||||
assert "BASE + 'static/my-work.js'" in source
|
||||
assert "BASE + 'static/dashboard.js'" in source
|
||||
assert "BASE + 'static/dashboard.css'" in source
|
||||
@@ -219,7 +219,7 @@ def test_resumable_today_session_ships_in_a_new_offline_shell():
|
||||
def test_mobile_conversation_photo_bundles_roll_the_offline_shell():
|
||||
source = WORKER.read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v140" in source
|
||||
assert "stackchain-dashboard-shell-v141" in source
|
||||
assert "BASE + 'static/dashboard.js'" in source
|
||||
assert "BASE + 'static/authored-outbox.js'" in source
|
||||
assert "BASE + 'static/background-issue-sync.js'" in source
|
||||
@@ -228,7 +228,7 @@ def test_mobile_conversation_photo_bundles_roll_the_offline_shell():
|
||||
def test_photo_metadata_sanitizer_rolls_the_cached_optimizer_atomically():
|
||||
source = WORKER.read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v140" in source
|
||||
assert "stackchain-dashboard-shell-v141" in source
|
||||
assert "BASE + 'static/issue-evidence-review.js'" in source
|
||||
assert "BASE + 'static/issue-attachment.js'" in source
|
||||
|
||||
@@ -236,14 +236,14 @@ def test_photo_metadata_sanitizer_rolls_the_cached_optimizer_atomically():
|
||||
def test_ownership_exit_runtime_rolls_the_offline_shell_cache():
|
||||
source = WORKER.read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v140" in source
|
||||
assert "stackchain-dashboard-shell-v141" in source
|
||||
assert "BASE + 'static/dashboard.js'" in source
|
||||
|
||||
|
||||
def test_offline_review_next_ships_today_completion_atomically():
|
||||
source = WORKER.read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v140" in source
|
||||
assert "stackchain-dashboard-shell-v141" in source
|
||||
assert "BASE + 'static/today-completion.js'" in source
|
||||
assert "BASE + 'static/dashboard.js'" in source
|
||||
|
||||
@@ -251,7 +251,7 @@ def test_offline_review_next_ships_today_completion_atomically():
|
||||
def test_duplicate_aware_capture_ships_in_a_new_offline_shell():
|
||||
source = WORKER.read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v140" in source
|
||||
assert "stackchain-dashboard-shell-v141" in source
|
||||
assert "BASE + 'static/create-issue-sheet.js'" in source
|
||||
assert "BASE + 'static/dashboard.js'" in source
|
||||
|
||||
@@ -259,7 +259,7 @@ def test_duplicate_aware_capture_ships_in_a_new_offline_shell():
|
||||
def test_inline_checklist_step_flow_rolls_the_offline_shell_atomically():
|
||||
source = WORKER.read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v140" in source
|
||||
assert "stackchain-dashboard-shell-v141" in source
|
||||
assert "BASE + 'static/issue-sheet.js'" in source
|
||||
assert "BASE + 'static/checklist-conflict.js'" in source
|
||||
assert "BASE + 'static/dashboard.js'" in source
|
||||
@@ -269,14 +269,14 @@ def test_inline_checklist_step_flow_rolls_the_offline_shell_atomically():
|
||||
def test_exact_later_picker_ships_atomically_in_a_new_offline_shell():
|
||||
source = WORKER.read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v140" in source
|
||||
assert "stackchain-dashboard-shell-v141" in source
|
||||
assert "BASE + 'static/later-picker.js'" in source
|
||||
|
||||
|
||||
def test_navigation_deadline_ships_in_a_new_shell_cache():
|
||||
source = WORKER.read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v140" in source
|
||||
assert "stackchain-dashboard-shell-v141" in source
|
||||
assert "BASE + 'static/dashboard.css'" in source
|
||||
assert "BASE + 'static/dashboard.js'" in source
|
||||
assert "BASE + 'static/install-app.js'" in source
|
||||
@@ -285,21 +285,21 @@ def test_navigation_deadline_ships_in_a_new_shell_cache():
|
||||
def test_today_convergence_ships_in_a_new_shell_cache():
|
||||
source = WORKER.read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v140" in source
|
||||
assert "stackchain-dashboard-shell-v141" in source
|
||||
assert "BASE + 'static/today-sync.js'" in source
|
||||
|
||||
|
||||
def test_mobile_search_viewport_ships_in_a_new_offline_shell():
|
||||
source = WORKER.read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v140" in source
|
||||
assert "stackchain-dashboard-shell-v141" in source
|
||||
assert "BASE + 'static/mobile-search-viewport.js'" in source
|
||||
|
||||
|
||||
def test_update_ownership_flow_ships_atomically_in_a_new_offline_shell():
|
||||
source = WORKER.read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v140" in source
|
||||
assert "stackchain-dashboard-shell-v141" in source
|
||||
assert "BASE + 'static/update-ownership.js'" in source
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@ def test_one_session_bound_csrf_proof_is_reused_for_a_background_drain():
|
||||
def test_queue_today_ships_atomically_in_a_new_offline_shell():
|
||||
source = WORKER.read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v140" in source
|
||||
assert "stackchain-dashboard-shell-v141" in source
|
||||
assert "BASE + 'static/queue-today.js'" in source
|
||||
|
||||
|
||||
@@ -1374,6 +1374,7 @@ def test_install_precaches_complete_subpath_scoped_app_shell():
|
||||
"/dashboard/static/dashboard.css",
|
||||
"/dashboard/static/dashboard.js",
|
||||
"/dashboard/static/human-gates.js",
|
||||
"/dashboard/static/progressive-human-gates.js",
|
||||
"/dashboard/static/icons/stackchain-192.png",
|
||||
"/dashboard/static/icons/stackchain-512.png",
|
||||
"/dashboard/static/session.js",
|
||||
|
||||
@@ -221,7 +221,7 @@ async def test_today_blocker_opens_existing_preview_and_preserves_readiness_gate
|
||||
def test_readiness_runtime_is_available_in_offline_shell():
|
||||
service_worker = SERVICE_WORKER.read_text()
|
||||
|
||||
assert "const CACHE = 'stackchain-dashboard-shell-v140';" in service_worker
|
||||
assert "const CACHE = 'stackchain-dashboard-shell-v141';" in service_worker
|
||||
assert "BASE + 'static/today-readiness.js'" in service_worker
|
||||
|
||||
|
||||
|
||||
@@ -343,7 +343,7 @@ listeners['stackchain:first-task-complete']();
|
||||
def test_inflight_today_drain_ships_in_a_new_offline_shell():
|
||||
source = (Path(__file__).parents[1] / "frontend" / "service-worker.js").read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v140" in source
|
||||
assert "stackchain-dashboard-shell-v141" in source
|
||||
assert "BASE + 'static/today-sync.js'" in source
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user