128 lines
5.1 KiB
Python
128 lines
5.1 KiB
Python
import json
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
|
|
CREATE_AND_START = Path(__file__).parents[1] / "frontend" / "create-and-start.js"
|
|
HTML = Path(__file__).parents[1] / "frontend" / "index.html"
|
|
DASHBOARD = Path(__file__).parents[1] / "frontend" / "dashboard.js"
|
|
CSS = Path(__file__).parents[1] / "frontend" / "dashboard.css"
|
|
WORKER = Path(__file__).parents[1] / "frontend" / "service-worker.js"
|
|
|
|
|
|
def run_node(script):
|
|
return json.loads(subprocess.run(
|
|
["node", "-e", script], check=True, capture_output=True, text=True
|
|
).stdout)
|
|
|
|
|
|
def test_confirmed_issue_is_planned_synced_warmed_and_started_once():
|
|
script = f"""
|
|
const fs = require('fs');
|
|
if (!fs.existsSync({json.dumps(str(CREATE_AND_START))})) {{
|
|
process.stdout.write(JSON.stringify({{available:false}}));
|
|
}} else {{
|
|
const createCreateAndStart = require({json.dumps(str(CREATE_AND_START))});
|
|
const calls = [];
|
|
const issue = {{kind:'issue', repository:'stackchain/dashboard', number:387}};
|
|
const flow = createCreateAndStart({{
|
|
todayWork: {{limit:5, read:()=>[], identity:item=>'issue:'+item.repository+':'+item.number+':', add:()=>{{calls.push('add');return 'added';}}}},
|
|
todaySync: {{enqueue:(action,id)=>{{calls.push(action+':'+id);return true;}}, flush:()=>calls.push('flush')}},
|
|
refresh:()=>calls.push('refresh'), warm:()=>calls.push('warm'), start:item=>calls.push('start:'+item.number),
|
|
announce:message=>calls.push('announce:'+message),
|
|
}});
|
|
const result = flow.complete(issue);
|
|
flow.complete(issue);
|
|
process.stdout.write(JSON.stringify({{available:true,result,calls}}));
|
|
}}
|
|
"""
|
|
|
|
assert run_node(script) == {
|
|
"available": True,
|
|
"result": "started",
|
|
"calls": [
|
|
"add",
|
|
"add:issue:stackchain/dashboard:387:",
|
|
"refresh",
|
|
"flush",
|
|
"warm",
|
|
"start:387",
|
|
"announce:Created, added to Today, and ready to work.",
|
|
],
|
|
}
|
|
|
|
|
|
def test_mobile_capture_wires_distinct_create_and_start_intent_into_the_offline_shell():
|
|
html = HTML.read_text()
|
|
dashboard = DASHBOARD.read_text()
|
|
css = CSS.read_text()
|
|
worker = WORKER.read_text()
|
|
|
|
assert '<button id="create-and-start-issue" type="submit">Create & start</button>' in html
|
|
assert '<script src="static/create-and-start.js"></script>' in html
|
|
assert "const createAndStart = createCreateAndStart({" in dashboard
|
|
assert "event.submitter?.id === 'create-and-start-issue'" in dashboard
|
|
assert "createAndStart.complete(created)" in dashboard
|
|
assert ".create-issue-actions" in css and "grid-template-columns" in css
|
|
assert "BASE + 'static/create-and-start.js'" in worker
|
|
|
|
|
|
def test_create_and_start_intent_is_durable_and_has_a_one_tap_relaunch_continuation():
|
|
dashboard = DASHBOARD.read_text()
|
|
|
|
assert "completionIntent: 'create-and-start'" in dashboard
|
|
assert "result.completions || []" in dashboard
|
|
assert "(result.completions || []).forEach(completion =>" in dashboard
|
|
assert "issueOutbox.pendingCompletions(activeFlushLogin)" in dashboard
|
|
assert "issueOutbox.completeIntent(completion.id, activeFlushLogin)" in dashboard
|
|
assert 'class="draft-continue"' in dashboard
|
|
assert "Continue created work" in dashboard
|
|
|
|
|
|
def test_create_and_start_stops_before_session_when_today_sync_cannot_be_queued():
|
|
script = f"""
|
|
const createCreateAndStart = require({json.dumps(str(CREATE_AND_START))});
|
|
const calls = [];
|
|
const issue = {{kind:'issue', repository:'stackchain/dashboard', number:388}};
|
|
const flow = createCreateAndStart({{
|
|
todayWork: {{limit:5, read:()=>[], identity:()=> 'issue:stackchain/dashboard:388:', add:()=> 'added'}},
|
|
todaySync: {{enqueue:()=>false, flush:()=>calls.push('flush')}},
|
|
refresh:()=>calls.push('refresh'), warm:()=>calls.push('warm'), start:()=>calls.push('start'),
|
|
announce:message=>calls.push(message),
|
|
}});
|
|
process.stdout.write(JSON.stringify({{result:flow.complete(issue), calls}}));
|
|
"""
|
|
|
|
assert run_node(script) == {
|
|
"result": "sync-unavailable",
|
|
"calls": ["Created and saved to Today on this device, but account sync is unavailable."],
|
|
}
|
|
|
|
|
|
def test_create_and_start_can_resume_after_today_sync_admission_recovers():
|
|
script = f"""
|
|
const createCreateAndStart = require({json.dumps(str(CREATE_AND_START))});
|
|
const calls=[];let admitted=false;let added=false;
|
|
const issue={{kind:'issue',repository:'stackchain/dashboard',number:389}};
|
|
const flow=createCreateAndStart({{
|
|
todayWork:{{limit:5,read:()=>[],identity:()=> 'issue:stackchain/dashboard:389:',add:()=>{{
|
|
const outcome=added?'exists':'added';added=true;return outcome;
|
|
}}}},
|
|
todaySync:{{enqueue:()=>admitted,flush:()=>calls.push('flush')}},
|
|
refresh:()=>calls.push('refresh'),warm:()=>calls.push('warm'),start:()=>calls.push('start'),
|
|
announce:message=>calls.push(message),
|
|
}});
|
|
const first=flow.complete(issue);
|
|
admitted=true;
|
|
const second=flow.complete(issue);
|
|
process.stdout.write(JSON.stringify({{first,second,calls}}));
|
|
"""
|
|
output = run_node(script)
|
|
|
|
assert output["first"] == "sync-unavailable"
|
|
assert output["second"] == "started"
|
|
assert output["calls"][-5:] == [
|
|
"refresh", "flush", "warm", "start",
|
|
"Created, added to Today, and ready to work.",
|
|
]
|