feat: pause Today for live data status (Closes #1138)
CI / lint (pull_request) Successful in 3m8s
CI / build-release (pull_request) Successful in 7s
CI / browser-journey (pull_request) Successful in 3m23s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-19 16:35:09 +00:00
1 parent 161563d6f5
commit 348f43f1bc
7 files changed
+250 -21

No files matched your search

+107
View File
@@ -81,10 +81,112 @@ const controller = status.createRefreshController({{
}
def test_live_data_status_sheet_pauses_today_contains_focus_and_closes_through_back():
script = f"""
const status = require({json.dumps(str(STATUS))});
function element(name) {{
return {{name, hidden:false, inert:false, listeners:{{}}, focused:0,
addEventListener(type, fn) {{ this.listeners[type] = fn; }},
focus() {{ this.focused += 1; }},
}};
}}
const trigger = element('trigger');
trigger.attrs = {{}};
trigger.setAttribute = (name, value) => trigger.attrs[name] = value;
const close = element('close');
const refresh = element('refresh');
const back = element('return');
const sheet = element('sheet');
sheet.hidden = true;
sheet.querySelectorAll = () => [close, refresh, back];
const header = element('header');
const main = element('main');
const listeners = {{}};
const history = {{state:null, pushes:0, backs:0,
pushState(state) {{ this.state=state; this.pushes += 1; }},
back() {{ this.backs += 1; }},
}};
const timerView = {{begins:[], finishes:0,
beginDetour(reason) {{ this.begins.push(reason); return {{identity:'issue:r:42', reason}}; }},
finishDetour() {{ this.finishes += 1; return {{resumed:true}}; }},
}};
const paused = element('paused');
paused.hidden = true;
const controller = status.createSheetController({{
sheet, trigger, closeButton:close, returnButton:back, pausedStatus:paused,
timerView, history, historyTarget:{{addEventListener:(name, fn) => listeners[name]=fn}},
escapeTarget:{{addEventListener:(name, fn) => listeners[name]=fn}},
backgroundElements:[header, main],
}});
controller.start();
controller.open();
const opened = {{hidden:sheet.hidden, expanded:trigger.attrs['aria-expanded'], paused:paused.hidden, inert:[header.inert, main.inert],
begins:timerView.begins, pushes:history.pushes, closeFocused:close.focused}};
let prevented = 0;
listeners.keydown({{key:'Tab', target:back, shiftKey:false, preventDefault:()=>prevented++}});
const trapped = {{prevented, closeFocused:close.focused}};
controller.close();
const requested = {{backs:history.backs, finishes:timerView.finishes}};
history.state = null;
listeners.popstate({{state:null}});
process.stdout.write(JSON.stringify({{opened, trapped, requested, closed:{{hidden:sheet.hidden, paused:paused.hidden,
expanded:trigger.attrs['aria-expanded'], inert:[header.inert, main.inert], finishes:timerView.finishes,
triggerFocused:trigger.focused}}}}));
"""
result = run_node(script)
assert result == {
"opened": {
"hidden": False,
"expanded": "true",
"paused": False,
"inert": [True, True],
"begins": ["live-data-status"],
"pushes": 1,
"closeFocused": 1,
},
"trapped": {"prevented": 1, "closeFocused": 2},
"requested": {"backs": 1, "finishes": 0},
"closed": {
"hidden": True,
"paused": True,
"expanded": "false",
"inert": [False, False],
"finishes": 1,
"triggerFocused": 1,
},
}
def test_live_data_status_without_running_today_hides_return_and_does_not_resume():
script = f"""
const status = require({json.dumps(str(STATUS))});
function element(hidden=false) {{ return {{hidden, inert:false, listeners:{{}}, attrs:{{}},
addEventListener(name, fn) {{ this.listeners[name]=fn; }}, focus() {{}},
setAttribute(name, value) {{ this.attrs[name]=value; }}, querySelectorAll() {{ return []; }} }}; }}
const sheet=element(true), trigger=element(), close=element(), returnButton=element();
let finishes=0;
const controller=status.createSheetController({{
sheet,trigger,closeButton:close,returnButton,
timerView:{{beginDetour:()=>null,finishDetour:()=>finishes++}},
}});
controller.start();
controller.open();
const opened={{returnHidden:returnButton.hidden, expanded:trigger.attrs['aria-expanded']}};
controller.close();
process.stdout.write(JSON.stringify({{opened, finishes}}));
"""
assert run_node(script) == {
"opened": {"returnHidden": True, "expanded": "true"},
"finishes": 0,
}
def test_live_data_status_has_accessible_mobile_safe_sheet_contract():
html = HTML.read_text()
css = CSS.read_text()
dashboard = DASHBOARD.read_text()
status_source = STATUS.read_text()
assert 'id="open-live-data-status"' in html
assert 'aria-controls="live-data-status-sheet"' in html
@@ -92,10 +194,15 @@ def test_live_data_status_has_accessible_mobile_safe_sheet_contract():
assert 'aria-labelledby="live-data-status-heading"' in html
assert 'id="live-data-status-feeds"' in html
assert 'id="refresh-live-data"' in html
assert 'id="return-from-live-data-status"' in html
assert 'id="live-data-status-today-paused"' in html
assert '<script src="static/live-data-status.js"></script>' in html
assert ".live-data-status-panel" in css
assert "width:min(560px,100%)" in css
assert "min-height:44px" in css
assert "liveDataStatus.describe" in dashboard
assert "liveDataStatus.createRefreshController" in dashboard
assert "liveDataStatus.mount" in dashboard
assert "createSheetController" in status_source
assert "backgroundElements:[qs('header'), qs('main'), qs('#mobile-task-dock')]" in status_source
assert "contextPoller.getState()" in dashboard
+29
View File
@@ -391,6 +391,35 @@ process.stdout.write(JSON.stringify({interruption, paused, returned, resumed, re
}
def test_live_data_status_detour_excludes_troubleshooting_time_and_resumes_once():
script = SOURCE.read_text() + r"""
const values = new Map();
const storage = {getItem:key=>values.get(key)||null,setItem:(key,value)=>values.set(key,value)};
let now = 1000;
const timer = createTodayTimer({storage, getLogin:()=> 'timmy', now:()=>now});
timer.activate('issue:r:42:');
now = 6000;
const interruption = timer.beginDetour('live-data-status');
now = 26000;
const paused = timer.snapshot();
const returned = timer.returnFromDetour('live-data-status');
now = 28000;
const resumed = timer.snapshot();
const repeated = timer.returnFromDetour('live-data-status');
process.stdout.write(JSON.stringify({interruption, paused, returned, resumed, repeated}));
"""
completed = subprocess.run(["node", "-e", script], capture_output=True, text=True)
assert completed.returncode == 0, completed.stderr
assert json.loads(completed.stdout) == {
"interruption": {"identity": "issue:r:42:", "resume": True, "reason": "live-data-status"},
"paused": {"identity": "issue:r:42:", "elapsed_ms": 5000, "running": False},
"returned": {"identity": "issue:r:42:", "resumed": True, "reason": "live-data-status"},
"resumed": {"identity": "issue:r:42:", "elapsed_ms": 7000, "running": True},
"repeated": None,
}
def test_mobile_detour_view_names_work_and_returns_to_today():
script = SOURCE.read_text() + r"""
const values = new Map();