import json import subprocess from pathlib import Path RECENT_WORK = Path(__file__).resolve().parents[1] / "frontend" / "mobile-recent-work.js" INDEX = Path(__file__).resolve().parents[1] / "frontend" / "index.html" DASHBOARD = Path(__file__).resolve().parents[1] / "frontend" / "dashboard.js" CSS = Path(__file__).resolve().parents[1] / "frontend" / "dashboard.css" def run_node(script: str) -> dict: result = subprocess.run(["node", "-e", script], capture_output=True, text=True) assert result.returncode == 0, result.stderr return json.loads(result.stdout) def test_recent_work_is_account_scoped_deduplicated_and_bounded(): script = f""" const createRecentWork = require({json.dumps(str(RECENT_WORK))}); const values = new Map(); const storage = {{ getItem:key => values.has(key) ? values.get(key) : null, setItem:(key, value) => values.set(key, value), removeItem:key => values.delete(key), }}; let login = ' Alice '; const recent = createRecentWork({{storage, getLogin:() => login, limit:5}}); for (let number=1; number<=6; number += 1) {{ recent.record({{kind:'issue', repository:'stackchain/dashboard', number, title:'Issue ' + number}}); }} recent.record({{kind:'issue', repository:'stackchain/dashboard', number:3, title:'Issue 3 updated'}}); const alice = recent.items(); login = 'bob'; recent.record({{kind:'pull', repository:'stackchain/api', number:9, title:'Ship API'}}); const bob = recent.items(); login = ''; const anonymousRecord = recent.record({{kind:'issue', repository:'stackchain/dashboard', number:99, title:'Private'}}); const anonymous = recent.items(); process.stdout.write(JSON.stringify({{alice,bob,anonymousRecord,anonymous,keys:Array.from(values.keys()).sort()}})); """ payload = run_node(script) assert [item["number"] for item in payload["alice"]] == [3, 6, 5, 4, 2] assert payload["alice"][0] == { "kind": "issue", "repository": "stackchain/dashboard", "number": 3, "title": "Issue 3 updated", "route": "#/my-work/issue/stackchain/dashboard/3", } assert payload["bob"] == [ { "kind": "pull", "repository": "stackchain/api", "number": 9, "title": "Ship API", "route": "#/my-work/pull/stackchain/api/9", } ] assert payload["anonymousRecord"] is False assert payload["anonymous"] == [] assert payload["keys"] == [ "stackchain.mobile-recent-work.v1.alice", "stackchain.mobile-recent-work.v1.bob", ] def test_recent_work_renders_safe_rows_and_opens_the_selected_route(): script = f""" const createRecentWork = require({json.dumps(str(RECENT_WORK))}); const values = new Map([ ['stackchain.mobile-recent-work.v1.alice', JSON.stringify([ {{kind:'issue',repository:'stackchain/dashboard',number:7,title:'Fix mobile queue',route:'#/wrong'}}, {{kind:'update',number:42,title:'Review release status'}}, {{kind:'pull',repository:'bad/repo/extra',number:1,title:'Unsafe'}}, {{kind:'issue',repository:'stackchain/dashboard',number:0,title:'Invalid'}}, ])], ]); function node(tag) {{ return {{tag,children:[],attributes:{{}},listeners:{{}},hidden:false,textContent:'', appendChild(child){{this.children.push(child);return child;}}, replaceChildren(...children){{this.children=children;}}, setAttribute(name,value){{this.attributes[name]=String(value);}}, addEventListener(name,callback){{this.listeners[name]=callback;}}, click(){{this.listeners.click?.();}}, }}; }} const list=node('div'); const section=node('section'); const opened=[]; const recent=createRecentWork({{ storage:{{getItem:key=>values.get(key)||null,setItem:(key,value)=>values.set(key,value)}}, getLogin:()=>'alice', document:{{createElement:node}}, list, section, openRoute:route=>opened.push(route), }}); const rendered=recent.render(); list.children[1].click(); process.stdout.write(JSON.stringify({{ rendered,hidden:section.hidden,rows:list.children.map(button=>({{ label:button.attributes['aria-label'],route:button.attributes['data-recent-work-route'], primary:button.children[0].children[0].textContent, secondary:button.children[0].children[1].textContent, }})),opened, }})); """ payload = run_node(script) assert payload == { "rendered": 2, "hidden": False, "rows": [ { "label": "Open Fix mobile queue, issue stackchain/dashboard #7", "route": "#/my-work/issue/stackchain/dashboard/7", "primary": "Fix mobile queue", "secondary": "Issue · stackchain/dashboard #7", }, { "label": "Open Review release status, update #42", "route": "#/my-work/update/42", "primary": "Review release status", "secondary": "Update · #42", }, ], "opened": ["#/my-work/update/42"], } def test_mobile_queues_integrates_recent_work_with_canonical_detail_routes(): html = INDEX.read_text() dashboard = DASHBOARD.read_text() css = CSS.read_text() assert 'id="mobile-recent-work"' in html assert 'id="mobile-recent-work-list"' in html assert '' in html assert "createMobileRecentWork({" in dashboard assert "mobileRecentWork.record(item)" in dashboard assert "mobileRecentWork.render()" in dashboard assert "workRoute.sync()" in dashboard assert "[data-recent-work-route]" in css assert "min-height:56px" in css