feat: prefetch the next mobile update (Closes #757)
This commit is contained in:
1 parent
1a3df5d7a7
commit
db08c7e75f
7 files changed
+141
-9
No files matched your search
@@ -68,8 +68,8 @@ def test_product_workflows_are_stable_lazy_feature_chunks(tmp_path):
|
||||
assert b"function attachSecurityCenter" in security_center.runtime_bytes
|
||||
assert b"gitea_time_logged" not in first.runtime_bytes
|
||||
assert b"gitea_time_logged" in security_center.runtime_bytes
|
||||
# The recap adds only startup wiring; its UI remains in the lazy Today bundle.
|
||||
assert len(first.runtime_gzip_bytes) <= 96 * 1024
|
||||
# One-ahead Updates prefetch stays in the core reader so transitions can reuse its in-flight request.
|
||||
assert len(first.runtime_gzip_bytes) <= 97 * 1024
|
||||
assert f'name="stackchain-feature-issue-capture" content="{capture.runtime_name}"' in first.dashboard_html
|
||||
assert f'name="stackchain-feature-pull-workflow" content="{pull_workflow.runtime_name}"' in first.dashboard_html
|
||||
assert f"BASE + '{capture.runtime_name}'" in first.service_worker_source
|
||||
|
||||
@@ -4871,6 +4871,76 @@ reader.open(original[0], original).then(() =>
|
||||
assert output["result"]["next"]["notification_id"] == 43
|
||||
|
||||
|
||||
def test_notification_reader_consumes_one_bounded_prefetch_without_duplicate_load():
|
||||
script = f"""
|
||||
const buildMyWork = require({json.dumps(str(MY_WORK))});
|
||||
const items = [1,2,3].map(notification_id => ({{kind:'update', notification_id, has_update:true}}));
|
||||
const loaded = [], details = [], statuses = [];
|
||||
const releases = {{}};
|
||||
const reader = buildMyWork.createNotificationReader({{
|
||||
load: id => {{
|
||||
loaded.push(id);
|
||||
if (id === 1) return Promise.resolve({{id}});
|
||||
return new Promise(resolve => {{ releases[id] = resolve; }});
|
||||
}},
|
||||
markRead: async () => {{}},
|
||||
onOpen: () => {{}}, onDetail: detail => details.push(detail.id), onItems: () => {{}},
|
||||
onStatus: status => statuses.push(status), onClose: () => {{}},
|
||||
}});
|
||||
(async () => {{
|
||||
await reader.open(items[0]);
|
||||
const first = reader.prefetch(items[1]);
|
||||
const duplicate = reader.prefetch(items[1]);
|
||||
const rejected = reader.prefetch(items[2]);
|
||||
const advancing = reader.open(items[1]);
|
||||
await Promise.resolve();
|
||||
releases[2]({{id:2}});
|
||||
await advancing;
|
||||
process.stdout.write(JSON.stringify({{
|
||||
loaded, details, same:first === duplicate, rejected, statuses,
|
||||
}}));
|
||||
}})();
|
||||
"""
|
||||
output = json.loads(subprocess.run(
|
||||
["node", "-e", script], check=True, capture_output=True, text=True
|
||||
).stdout)
|
||||
|
||||
assert output == {
|
||||
"loaded": [1, 2],
|
||||
"details": [1, 2],
|
||||
"same": True,
|
||||
"rejected": False,
|
||||
"statuses": ["Loading update…", "Update ready.", "Update ready."],
|
||||
}
|
||||
|
||||
|
||||
def test_notification_reader_never_consumes_prefetch_from_another_account_scope():
|
||||
script = f"""
|
||||
const buildMyWork = require({json.dumps(str(MY_WORK))});
|
||||
let scope = 'timmy';
|
||||
const loaded = [];
|
||||
const reader = buildMyWork.createNotificationReader({{
|
||||
load: async id => {{ loaded.push([scope, id]); return {{owner:scope, id}}; }},
|
||||
getScope: () => scope,
|
||||
markRead: async () => {{}}, onOpen: () => {{}}, onItems: () => {{}}, onStatus: () => {{}},
|
||||
onDetail: detail => {{ if (detail.owner !== scope) throw new Error('cross-account detail'); }},
|
||||
onClose: () => {{}},
|
||||
}});
|
||||
(async () => {{
|
||||
await reader.open({{notification_id:1}});
|
||||
await reader.prefetch({{notification_id:2}});
|
||||
scope = 'alexander';
|
||||
const opened = await reader.open({{notification_id:2}});
|
||||
process.stdout.write(JSON.stringify({{loaded, opened}}));
|
||||
}})();
|
||||
"""
|
||||
output = json.loads(subprocess.run(
|
||||
["node", "-e", script], check=True, capture_output=True, text=True
|
||||
).stdout)
|
||||
|
||||
assert output == {"loaded": [["timmy", 1], ["timmy", 2], ["alexander", 2]], "opened": True}
|
||||
|
||||
|
||||
def test_notification_reader_acknowledges_once_and_opens_next_update():
|
||||
script = f"""
|
||||
const buildMyWork = require({json.dumps(str(MY_WORK))});
|
||||
|
||||
@@ -81,6 +81,27 @@ process.stdout.write(JSON.stringify({opened, progress, stored:values.get('stackc
|
||||
}
|
||||
|
||||
|
||||
def test_update_triage_exposes_only_the_next_surviving_snapshot_item():
|
||||
result = run_session("""
|
||||
const values = new Map();
|
||||
let items = [1,2,3].map(notification_id => ({notification_id}));
|
||||
const session = createSession({
|
||||
storage:{getItem:key=>values.get(key)||null,setItem:(key,value)=>values.set(key,value),removeItem:key=>values.delete(key)},
|
||||
getLogin:()=> 'timmy', getItems:()=>items,
|
||||
onOpen:()=>{}, onProgress:()=>{}, onFinish:()=>{},
|
||||
});
|
||||
session.start();
|
||||
const first = session.next()?.notification_id;
|
||||
items = [1,3,4].map(notification_id => ({notification_id}));
|
||||
const afterRemoval = session.next()?.notification_id;
|
||||
session.keepUnreadAndNext();
|
||||
const afterAdvance = session.next();
|
||||
process.stdout.write(JSON.stringify({first, afterRemoval, afterAdvance:afterAdvance || null}));
|
||||
""")
|
||||
|
||||
assert result == {"first": 2, "afterRemoval": 3, "afterAdvance": None}
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_dashboard_wires_resumable_updates_triage_mobile_flow():
|
||||
html = await dashboard()
|
||||
|
||||
Reference in new issue
Block a user