108 lines
4.3 KiB
Python
108 lines
4.3 KiB
Python
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
|