feat: pause Today during mobile Search (Closes #1078)
CI / lint (pull_request) Successful in 3m5s
CI / build-release (pull_request) Successful in 7s
CI / browser-journey (pull_request) Successful in 2m21s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-18 12:45:08 +00:00
1 parent ff34416c3c
commit d4910b4398
7 files changed
+209 -4

No files matched your search

@@ -120,6 +120,30 @@ def test_release_artifact_plans_hands_off_and_opens_next_mobile_issue(tmp_path:
assert resumed == {"running": True, "saved": True}
assert page.evaluate("document.documentElement.scrollWidth <= window.innerWidth")
page.locator('[data-mobile-task="search"]').click()
search_pause = page.locator('#cmd-palette [data-search-today-interruption]')
expect(search_pause).to_be_visible()
expect(search_pause).to_contain_text("Today paused · Ship mobile capture")
return_button = search_pause.locator('[data-return-from-search]')
bounds = return_button.bounding_box()
assert bounds and bounds["height"] >= 44
assert page.evaluate("""() => {
const key = Object.keys(localStorage).find(value => value.startsWith('stackchain.today-timer.v1.'));
const timer = JSON.parse(localStorage.getItem(key));
return timer.entries[timer.active_identity].running === false &&
timer.search_interruption?.identity === timer.active_identity;
}""")
assert page.evaluate("document.documentElement.scrollWidth <= window.innerWidth")
return_button.click()
expect(page.locator("#cmd-palette")).not_to_have_class("open")
expect(page.locator("#issue-sheet")).to_have_class("issue-sheet open")
assert page.evaluate("""() => {
const key = Object.keys(localStorage).find(value => value.startsWith('stackchain.today-timer.v1.'));
const timer = JSON.parse(localStorage.getItem(key));
return timer.entries[timer.active_identity].running === true && !timer.search_interruption;
}""")
page.locator("#close-issue-sheet").click()
page.locator("[data-mobile-today-more]").click()
expect(page.locator("#mobile-today-actions")).to_be_visible()
blocked = page.locator("[data-mobile-today-blocked]")
+11
View File
@@ -951,6 +951,17 @@ def test_search_preview_offers_assign_and_start_for_eligible_issues():
assert "grid-template-columns:repeat(2,minmax(0,1fr))" in css
def test_mobile_search_pauses_today_and_offers_a_lossless_return():
html = dashboard_bundle_text()
css = (FRONTEND / "dashboard.css").read_text()
assert html.count('<aside class="search-today-interruption" data-search-today-interruption') == 2
assert html.count('<button data-return-from-search type="button">') == 2
assert "taskOverlayHistory.leave();" in html
assert ".search-today-interruption" in css
assert ".search-today-interruption button { min-height:44px;" in css
def test_search_preview_queues_eligible_issue_and_continues_preserved_search():
html = dashboard_bundle_text()
css = (FRONTEND / "dashboard.css").read_text()
+54
View File
@@ -173,3 +173,57 @@ process.stdout.write(JSON.stringify({opened, shown, finished, closed, transferre
"transferred": {"identity": "issue:r:1:", "resumed": False},
"final": {"identity": "issue:r:1:", "elapsed_ms": 0, "running": False},
}
def test_search_leaves_manually_paused_work_alone_and_cannot_restore_replaced_work():
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.adopt('issue:r:1:', 3000, false);
const pausedSearch = timer.beginSearch();
timer.activate('issue:r:1:');
const runningSearch = timer.beginSearch();
timer.activate('issue:r:2:');
const replacedReturn = timer.returnFromSearch();
process.stdout.write(JSON.stringify({pausedSearch, runningSearch, replacedReturn, snapshot:timer.snapshot()}));
"""
completed = subprocess.run(["node", "-e", script], capture_output=True, text=True)
assert completed.returncode == 0, completed.stderr
assert json.loads(completed.stdout) == {
"pausedSearch": None,
"runningSearch": {"identity": "issue:r:1:", "resume": True},
"replacedReturn": None,
"snapshot": {"identity": "issue:r:2:", "elapsed_ms": 0, "running": True},
}
def test_search_pauses_running_today_time_and_restores_exactly_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.beginSearch();
now = 26000;
const paused = timer.snapshot();
const returned = timer.returnFromSearch();
now = 28000;
const restored = timer.snapshot();
const repeated = timer.returnFromSearch();
process.stdout.write(JSON.stringify({interruption, paused, returned, restored, 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},
"paused": {"identity": "issue:r:42:", "elapsed_ms": 5000, "running": False},
"returned": {"identity": "issue:r:42:", "resumed": True},
"restored": {"identity": "issue:r:42:", "elapsed_ms": 7000, "running": True},
"repeated": None,
}