stackchain-dashboard/tests/test_update_read_position.py
timmy 7662cca9e6
All checks were successful
CI / lint (pull_request) Successful in 3m36s
CI / build-release (pull_request) Successful in 6s
CI / browser-journey (pull_request) Successful in 6m19s
CI / release-candidate (pull_request) Has been skipped
feat: act on Search and Following comments (Closes #1388)
2026-08-25 09:11:14 +00:00

89 lines
3.4 KiB
Python

import json
import subprocess
from pathlib import Path
import pytest
from tests.dashboard_bundle import dashboard
POSITIONER = Path(__file__).resolve().parents[1] / "frontend" / "update-read-position.js"
def run_positioner(script: str) -> dict:
source = f"const createPositioner = require({json.dumps(str(POSITIONER))});\n" + script
result = subprocess.run(["node", "-e", source], capture_output=True, text=True)
assert result.returncode == 0, result.stderr
return json.loads(result.stdout)
def test_new_update_scrolls_to_newest_activity_once_without_reusing_previous_offset():
result = run_positioner("""
const listeners = {};
const panel = {scrollTop:480, addEventListener:(name, fn)=>listeners[name]=fn};
const jump = {hidden:true, focus(){}};
const first = {calls:0, scrollIntoView(){this.calls += 1}, classList:{add(){}}};
const second = {calls:0, scrollIntoView(){this.calls += 1}, classList:{add(){}}};
const positioner = createPositioner({panel, jump});
positioner.open('101');
positioner.ready('101', first);
positioner.ready('101', first);
panel.scrollTop = 920;
positioner.open('102');
positioner.ready('102', second);
process.stdout.write(JSON.stringify({first:first.calls, second:second.calls, jumpHidden:jump.hidden}));
""")
assert result == {"first": 1, "second": 1, "jumpHidden": True}
def test_user_scroll_during_loading_preserves_position_until_explicit_jump():
result = run_positioner("""
const listeners = {};
const panel = {scrollTop:120, addEventListener:(name, fn)=>listeners[name]=fn};
const jump = {hidden:true, focus(){}};
const target = {calls:0, scrollIntoView(){this.calls += 1}, classList:{add(){}}};
const positioner = createPositioner({panel, jump});
positioner.open('201');
panel.scrollTop = 260;
listeners.scroll();
positioner.ready('201', target);
const before = {calls:target.calls, hidden:jump.hidden, top:panel.scrollTop};
positioner.jump();
process.stdout.write(JSON.stringify({before, after:{calls:target.calls, hidden:jump.hidden}}));
""")
assert result == {
"before": {"calls": 0, "hidden": False, "top": 260},
"after": {"calls": 1, "hidden": True},
}
def test_stale_or_prefetched_conversation_cannot_move_the_visible_update():
result = run_positioner("""
const panel = {scrollTop:0, addEventListener(){}};
const jump = {hidden:true};
const stale = {calls:0, scrollIntoView(){this.calls += 1}, classList:{add(){}}};
const current = {calls:0, scrollIntoView(){this.calls += 1}, classList:{add(){}}};
const positioner = createPositioner({panel, jump});
positioner.open('302');
positioner.ready('301', stale);
positioner.ready('302', current);
process.stdout.write(JSON.stringify({stale:stale.calls, current:current.calls}));
""")
assert result == {"stale": 0, "current": 1}
@pytest.mark.anyio
async def test_dashboard_mounts_mobile_new_activity_positioning_flow():
html = await dashboard()
assert '<script src="static/update-read-position.js"></script>' in html
assert 'id="jump-update-new-activity"' in html
assert "updateReadPosition.open(String(item.notification_id))" in html
source = html + (POSITIONER.parent / "conversation.js").read_text()
assert "updateReadPosition.ready(String(getSelectedUpdate()?.notification_id || ''),newest)" in source
assert ".update-new-activity" in html
assert "BASE + 'static/update-read-position.js'" in (POSITIONER.parent / "service-worker.js").read_text()