feat: resume addressable mobile search sessions (Closes #781)
This commit is contained in:
1 parent
741c27eacf
commit
bc0d47e8e7
4 files changed
+221
-26
No files matched your search
@@ -212,3 +212,88 @@ def test_dashboard_routes_mobile_task_overlays_through_browser_history():
|
||||
assert "taskOverlayHistory.close()" in html
|
||||
assert "saveIssueCaptureDraft();" in html
|
||||
assert "history.replaceState(history.state || {}, '', cleanUrl)" in html
|
||||
|
||||
|
||||
def test_search_history_serializes_bounded_query_and_stable_preview_identity():
|
||||
script = f"""
|
||||
const createTaskOverlayHistory = require({json.dumps(str(OVERLAY_HISTORY))});
|
||||
const pushed = [];
|
||||
const replaced = [];
|
||||
const location = {{ pathname:'/dashboard/', search:'?keep=1', hash:'' }};
|
||||
const history = {{
|
||||
state: {{ page:'dashboard' }},
|
||||
pushState(state, title, url) {{ this.state = state; pushed.push([state, url]); }},
|
||||
replaceState(state, title, url) {{ this.state = state; replaced.push([state, url]); }},
|
||||
back() {{}},
|
||||
}};
|
||||
const controller = createTaskOverlayHistory({{
|
||||
history, location, eventTarget:{{addEventListener() {{}}}}, onChange() {{}},
|
||||
}});
|
||||
controller.open('search', {{ query:' release blocker ' }});
|
||||
controller.update({{ query:'release candidate' }});
|
||||
controller.open('search-preview', {{
|
||||
query:'release candidate', preview:{{kind:'issue', repository:'stackchain/api', number:123,
|
||||
title:'must not be serialized', body:'secret'}},
|
||||
}});
|
||||
process.stdout.write(JSON.stringify({{pushed, replaced, current:controller.currentState()}}));
|
||||
"""
|
||||
result = subprocess.run(["node", "-e", script], capture_output=True, text=True)
|
||||
|
||||
assert result.returncode == 0, result.stderr
|
||||
payload = json.loads(result.stdout)
|
||||
assert len(payload["pushed"]) == 2
|
||||
assert len(payload["replaced"]) == 1
|
||||
assert payload["pushed"][0][1] == "/dashboard/?keep=1&search=release+blocker"
|
||||
assert payload["replaced"][0][1] == "/dashboard/?keep=1&search=release+candidate"
|
||||
assert payload["pushed"][1][1] == (
|
||||
"/dashboard/?keep=1&search=release+candidate&preview=issue%3Astackchain%2Fapi%3A123"
|
||||
)
|
||||
assert payload["current"] == {
|
||||
"kind": "search-preview",
|
||||
"query": "release candidate",
|
||||
"preview": {"kind": "issue", "repository": "stackchain/api", "number": 123},
|
||||
}
|
||||
|
||||
|
||||
def test_search_history_restores_direct_url_and_rejects_malformed_or_oversized_state():
|
||||
script = f"""
|
||||
const createTaskOverlayHistory = require({json.dumps(str(OVERLAY_HISTORY))});
|
||||
function restore(search) {{
|
||||
const changes = [];
|
||||
const location = {{ pathname:'/dashboard/', search, hash:'' }};
|
||||
const history = {{ state:null, replaceState(state) {{ this.state = state; }}, back() {{}} }};
|
||||
const controller = createTaskOverlayHistory({{
|
||||
history, location, eventTarget:{{addEventListener() {{}}}},
|
||||
onChange(kind, previous, detail) {{ changes.push([kind, previous, detail]); }},
|
||||
}});
|
||||
controller.start();
|
||||
return {{ state:controller.currentState(), changes }};
|
||||
}}
|
||||
process.stdout.write(JSON.stringify({{
|
||||
valid:restore('?search=release+blocker&preview=pull%3Astackchain%2Fapi%3A9'),
|
||||
malformed:restore('?search=ok&preview=issue%3Ainvalid%3A0'),
|
||||
oversized:restore('?search=' + 'x'.repeat(201)),
|
||||
}}));
|
||||
"""
|
||||
result = subprocess.run(["node", "-e", script], capture_output=True, text=True)
|
||||
|
||||
assert result.returncode == 0, result.stderr
|
||||
payload = json.loads(result.stdout)
|
||||
expected = {
|
||||
"kind": "search-preview",
|
||||
"query": "release blocker",
|
||||
"preview": {"kind": "pull", "repository": "stackchain/api", "number": 9},
|
||||
}
|
||||
assert payload["valid"]["state"] == expected
|
||||
assert payload["valid"]["changes"] == [["search-preview", None, expected]]
|
||||
assert payload["malformed"]["state"] == {"kind": "search", "query": "ok"}
|
||||
assert payload["oversized"]["state"] == {"kind": None}
|
||||
|
||||
|
||||
def test_dashboard_persists_search_query_and_restores_canonical_preview():
|
||||
html = dashboard_bundle_text()
|
||||
|
||||
assert "taskOverlayHistory.update({ query:e.target.value })" in html
|
||||
assert "taskOverlayHistory.open('search-preview', { query:qs('#cmd-input').value, preview:item.result })" in html
|
||||
assert "detail?.query" in html
|
||||
assert "searchPreview.open(detail.preview).catch" in html
|
||||
Reference in new issue
Block a user