import json import subprocess from pathlib import Path ROOT = Path(__file__).parents[1] MODULE = ROOT / "frontend" / "following.js" def run(script: str) -> dict: harness = f""" const createFollowing = require({json.dumps(str(MODULE))}); const state = {{ renders:[], counts:[], opened:[], requests:[] }}; const feature = createFollowing({{ fetchJson: async path => {{ state.requests.push(path); return {{revision:3,items:[{{repository:'stackchain/api',number:42,title:'Quiet issue',state:'open',updated_at:'2026-08-23T03:00:00Z',url:'https://forge.example/issue/42',has_unseen_change:false}}]}}; }}, render: snapshot => state.renders.push(snapshot), onCount: count => state.counts.push(count), onOpen: item => state.opened.push(item), }}); (async () => {{ {script} }})().catch(error => {{ console.error(error); process.exit(1); }}); """ completed = subprocess.run(["node", "-e", harness], text=True, capture_output=True, check=True) return json.loads(completed.stdout) def test_following_loads_account_collection_and_opens_existing_preview(): result = run(""" await feature.load(); feature.open(0); process.stdout.write(JSON.stringify(state)); """) assert result["requests"] == ["api/v1/following"] assert result["counts"] == [0] assert result["renders"][-1]["status"] == "ready" assert result["renders"][-1]["items"][0]["title"] == "Quiet issue" assert result["opened"] == [{ "repository": "stackchain/api", "number": 42, "title": "Quiet issue", "state": "open", "updated_at": "2026-08-23T03:00:00Z", "url": "https://forge.example/issue/42", "has_unseen_change": False, "kind": "issue", }] def test_following_opens_explicitly_but_never_becomes_work_recommendation(): launcher = ROOT / "frontend" / "mobile-queue-launcher.js" script = f""" const createLauncher = require({json.dumps(str(launcher))}); const calls = []; const feature = createLauncher({{ getCounts:() => ({{following:7}}), openFollowing:() => {{ calls.push('following'); return 'opened-following'; }}, selectFilter:name => calls.push(name), firstAction:() => null, announce:() => {{}}, openFindWork:() => calls.push('find'), }}); process.stdout.write(JSON.stringify({{opened:feature.open('following'),recommended:feature.recommend(),calls}})); """ result = json.loads(subprocess.run( ["node", "-e", script], text=True, capture_output=True, check=True ).stdout) assert result == { "opened": "opened-following", "recommended": {"name": "find", "count": 0, "label": "Find Work"}, "calls": ["following"], } def test_following_counts_unseen_changes_and_acknowledges_after_preview_loads(): script = f""" const createFollowing = require({json.dumps(str(MODULE))}); const state = {{counts:[], opened:[], acknowledged:[], renders:[]}}; let finishPreview; const feature = createFollowing({{ fetchJson:async () => ({{revision:4,items:[ {{repository:'stackchain/api',number:42,title:'Changed',state:'open',updated_at:'2026-08-23T04:00:00Z',url:'https://forge/42',has_unseen_change:true}}, {{repository:'stackchain/api',number:43,title:'Quiet',state:'open',updated_at:'2026-08-23T03:00:00Z',url:'https://forge/43',has_unseen_change:false}} ]}}), render:value => state.renders.push(value), onCount:value => state.counts.push(value), onOpen:item => new Promise(resolve => {{ finishPreview=() => {{ state.opened.push(item.number); resolve(); }}; }}), onAcknowledge:async item => state.acknowledged.push(item.updated_at), }}); (async () => {{ await feature.load(); const opening=feature.open(0); await new Promise(resolve => setImmediate(resolve)); state.before={{acknowledged:[...state.acknowledged],counts:[...state.counts]}}; finishPreview(); await opening; process.stdout.write(JSON.stringify(state)); }})().catch(error => {{ console.error(error); process.exit(1); }}); """ result = json.loads(subprocess.run( ["node", "-e", script], text=True, capture_output=True, check=True ).stdout) assert result["before"] == {"acknowledged": [], "counts": [1]} assert result["opened"] == [42] assert result["acknowledged"] == ["2026-08-23T04:00:00Z"] assert result["counts"] == [1, 0] assert result["renders"][-1]["items"][0]["has_unseen_change"] is False