feat: reopen recent work from mobile queues (Closes #1471)
This commit is contained in:
1 parent
46e06c1a37
commit
eb101bf5ce
8 files changed
+268
-2
No files matched your search
@@ -0,0 +1,141 @@
|
||||
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 '<script src="static/mobile-recent-work.js"></script>' 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
|
||||
@@ -1527,6 +1527,7 @@ def test_install_precaches_complete_subpath_scoped_app_shell():
|
||||
"/dashboard/static/mobile-task-dock.js",
|
||||
"/dashboard/static/mobile-first-task.js",
|
||||
"/dashboard/static/mobile-work-entry.js",
|
||||
"/dashboard/static/mobile-recent-work.js",
|
||||
"/dashboard/static/mobile-queue-priority.js",
|
||||
"/dashboard/static/mobile-queue-launcher.js",
|
||||
"/dashboard/static/mobile-delivery-recovery.js",
|
||||
|
||||
Reference in new issue
Block a user