feat: work Today blockers in-app (#443)
This commit is contained in:
1 parent
60bc7cd686
commit
a0aa3c0a22
11 files changed
+118
-23
No files matched your search
@@ -347,5 +347,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-v74" in source
|
||||
assert "stackchain-dashboard-shell-v75" in source
|
||||
assert "BASE + 'static/later-sync.js'" in source
|
||||
@@ -137,4 +137,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-v74" in worker
|
||||
assert "stackchain-dashboard-shell-v75" in worker
|
||||
@@ -35,4 +35,4 @@ 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-v74" in worker
|
||||
assert "stackchain-dashboard-shell-v75" in worker
|
||||
@@ -232,6 +232,6 @@ async def test_plan_today_wires_cancel_back_and_success_through_overlay_history(
|
||||
def test_plan_today_controller_is_available_in_the_offline_shell():
|
||||
source = SERVICE_WORKER.read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v74" in source
|
||||
assert "stackchain-dashboard-shell-v75" in source
|
||||
assert "BASE + 'static/plan-today.js'" in source
|
||||
assert "BASE + 'static/plan-today-preview.js'" in source
|
||||
@@ -122,7 +122,7 @@ async function dispatchNotificationClick(route) {{
|
||||
def test_resumable_today_session_ships_in_a_new_offline_shell():
|
||||
source = WORKER.read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v74" in source
|
||||
assert "stackchain-dashboard-shell-v75" in source
|
||||
assert "BASE + 'static/my-work.js'" in source
|
||||
assert "BASE + 'static/dashboard.js'" in source
|
||||
assert "BASE + 'static/dashboard.css'" in source
|
||||
@@ -131,7 +131,7 @@ def test_resumable_today_session_ships_in_a_new_offline_shell():
|
||||
def test_offline_review_next_ships_today_completion_atomically():
|
||||
source = WORKER.read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v74" in source
|
||||
assert "stackchain-dashboard-shell-v75" in source
|
||||
assert "BASE + 'static/today-completion.js'" in source
|
||||
assert "BASE + 'static/dashboard.js'" in source
|
||||
|
||||
@@ -139,7 +139,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-v74" in source
|
||||
assert "stackchain-dashboard-shell-v75" in source
|
||||
assert "BASE + 'static/create-issue-sheet.js'" in source
|
||||
assert "BASE + 'static/dashboard.js'" in source
|
||||
|
||||
@@ -147,14 +147,14 @@ def test_duplicate_aware_capture_ships_in_a_new_offline_shell():
|
||||
def test_exact_later_picker_ships_atomically_in_a_new_offline_shell():
|
||||
source = WORKER.read_text()
|
||||
|
||||
assert "stackchain-dashboard-shell-v74" in source
|
||||
assert "stackchain-dashboard-shell-v75" 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-v74" in source
|
||||
assert "stackchain-dashboard-shell-v75" in source
|
||||
assert "BASE + 'static/dashboard.css'" in source
|
||||
assert "BASE + 'static/dashboard.js'" in source
|
||||
assert "BASE + 'static/install-app.js'" in source
|
||||
@@ -163,21 +163,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-v74" in source
|
||||
assert "stackchain-dashboard-shell-v75" 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-v74" in source
|
||||
assert "stackchain-dashboard-shell-v75" 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-v74" in source
|
||||
assert "stackchain-dashboard-shell-v75" in source
|
||||
assert "BASE + 'static/update-ownership.js'" in source
|
||||
|
||||
|
||||
@@ -358,7 +358,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-v74" in source
|
||||
assert "stackchain-dashboard-shell-v75" in source
|
||||
assert "BASE + 'static/queue-today.js'" in source
|
||||
|
||||
|
||||
|
||||
@@ -102,6 +102,41 @@ readiness.run('resume', items, items[0]).then(async result => {{
|
||||
}
|
||||
|
||||
|
||||
def test_readiness_previews_only_a_current_blocker_without_closing_or_reordering_gate():
|
||||
script = f"""
|
||||
const createTodayReadiness = require({json.dumps(str(READINESS))});
|
||||
const previews = [];
|
||||
const items = [1, 2].map(number => ({{kind:'issue', repository:'r', number}}));
|
||||
const blocker = {{repository:'r', number:99, title:'Prerequisite', state:'open'}};
|
||||
const readiness = createTodayReadiness({{
|
||||
inspect: async candidate => candidate.number === 1
|
||||
? {{available:true, dependencies:[blocker]}}
|
||||
: {{available:true, dependencies:[]}},
|
||||
onPreview: candidate => previews.push(candidate.number),
|
||||
}});
|
||||
(async () => {{
|
||||
await readiness.run('start', items, items[0]);
|
||||
const accepted = readiness.previewBlocker(blocker);
|
||||
const rejected = readiness.previewBlocker({{repository:'r', number:100}});
|
||||
const snapshot = readiness.snapshot();
|
||||
process.stdout.write(JSON.stringify({{
|
||||
accepted, rejected, previews,
|
||||
open:snapshot.status === 'blocked',
|
||||
target:snapshot.target.number,
|
||||
order:snapshot.items.map(item => item.number),
|
||||
}}));
|
||||
}})();
|
||||
"""
|
||||
assert json.loads(run_node(script)) == {
|
||||
"accepted": True,
|
||||
"rejected": False,
|
||||
"previews": [99],
|
||||
"open": True,
|
||||
"target": 1,
|
||||
"order": [1, 2],
|
||||
}
|
||||
|
||||
|
||||
def test_readiness_never_calls_unknown_ready_and_supports_retry_or_explicit_override():
|
||||
script = f"""
|
||||
const createTodayReadiness = require({json.dumps(str(READINESS))});
|
||||
@@ -170,10 +205,23 @@ async def test_dashboard_exposes_mobile_today_readiness_gate_and_runtime():
|
||||
assert "runTodayTransition('complete'" in html
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_today_blocker_opens_existing_preview_and_preserves_readiness_gate():
|
||||
html = await dashboard()
|
||||
|
||||
assert 'data-today-blocker-index="' in html
|
||||
assert "todayReadiness.previewBlocker(blocker)" in html
|
||||
assert "searchPreview.open({ ...blocker, kind:'issue' })" in html
|
||||
assert "taskOverlayHistory.open('search-preview')" in html
|
||||
assert "suspendTodayReadiness()" in html
|
||||
assert "todayReadiness.cancel()" in html
|
||||
assert "todayReadinessBlockerFocus" in html
|
||||
|
||||
|
||||
def test_readiness_runtime_is_available_in_offline_shell():
|
||||
service_worker = SERVICE_WORKER.read_text()
|
||||
|
||||
assert "const CACHE = 'stackchain-dashboard-shell-v74';" in service_worker
|
||||
assert "const CACHE = 'stackchain-dashboard-shell-v75';" in service_worker
|
||||
assert "BASE + 'static/today-readiness.js'" in service_worker
|
||||
|
||||
|
||||
@@ -185,3 +233,5 @@ def test_mobile_readiness_gate_has_touch_safe_wrapping_actions():
|
||||
assert ".today-readiness-item" in css
|
||||
assert "overflow-wrap:anywhere" in css
|
||||
assert "env(safe-area-inset-bottom)" in css
|
||||
assert ".today-readiness-blocker" in css
|
||||
assert "min-height:44px" in css
|
||||
@@ -86,7 +86,7 @@ sync.enqueue('add', 'issue:r:1:');
|
||||
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-v74" in source
|
||||
assert "stackchain-dashboard-shell-v75" in source
|
||||
assert "BASE + 'static/today-sync.js'" in source
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user