Keep project quick-create filter changes in sync with session creation outcomes

This commit is contained in:
Rod Boev authored and nesquena-hermes committed 2026-06-30 19:28:27 +00:00
1 parent 08c4004271
commit a109f86946
2 files changed
+92 -22

No files matched your search

+12 -2
View File
@@ -6165,10 +6165,20 @@ function _attachProjectQuickCreateButton(chip, project){
if(typeof e.stopPropagation==='function') e.stopPropagation();
if(typeof e.stopImmediatePropagation==='function') e.stopImmediatePropagation();
};
btn.onclick=(e)=>{
btn.onclick=async(e)=>{
stop(e);
if(_newSessionInFlight){
await newSession(false,{project_id:project.project_id});
return;
}
const previousProject=(typeof _activeProject!=='undefined')?_activeProject:NO_PROJECT_FILTER;
_setActiveProjectFilter(project.project_id);
void newSession(false,{project_id:project.project_id});
try{
await newSession(false,{project_id:project.project_id});
}catch(err){
_setActiveProjectFilter(previousProject);
if(typeof showToast==='function') showToast('New conversation failed: '+(err&&err.message||err));
}
};
btn.ondblclick=(e)=>{stop(e);};
btn.oncontextmenu=(e)=>{stop(e);};
@@ -48,6 +48,8 @@ def test_quick_create_button_attaches_filter_align_and_request_path():
assert "project-chip-quick-create" in helper
assert "_setActiveProjectFilter(project.project_id)" in helper
assert "newSession(false,{project_id:project.project_id})" in helper
assert "if(_newSessionInFlight)" in helper
assert "_setActiveProjectFilter(previousProject)" in helper
assert "btn.ondblclick" in helper
assert "btn.oncontextmenu" in helper
assert "btn.ontouchstart" in helper
@@ -238,13 +240,27 @@ globalThis.document = {
},
};
globalThis._setActiveProjectFilter = (projectId) => {
globalThis._activeProject = projectId;
params.filterProjectId = projectId;
params.calls.push({type: 'set-filter', projectId});
};
globalThis._activeProject = params.activeProject;
globalThis.newSession = async (flash, options) => {
if (globalThis._newSessionInFlight) {
params.toasts.push('New conversation already in progress');
return globalThis._newSessionInFlight;
}
if (params.failNewSession) throw new Error(params.failMessage || 'request failed');
params.newSession = {flash, options};
params.calls.push({type: 'new-session', flash, options});
return params.newSessionResult || { session_id: 's-1' };
};
globalThis.showToast = (message) => {
params.toasts.push(String(message || ''));
};
globalThis._newSessionInFlight = params.newSessionInFlight
? Promise.resolve(params.newSessionInFlight)
: null;
eval(extractFunction(sessionsSrc, '_attachProjectQuickCreateButton'));
@@ -264,31 +280,45 @@ const touchEv = {
preventDefault() { params.touchPreventCount++; },
stopImmediatePropagation() { params.touchStopImmediateCount++; },
};
btn.onclick(ev);
btn.ondblclick(ev);
btn.oncontextmenu(ev);
btn.ontouchstart(touchEv);
btn.ontouchend(touchEv);
console.log(JSON.stringify({
buttonClass: btn.className,
buttonTag: btn.tagName,
buttonText: btn.textContent,
newSession: params.newSession,
filterProjectId: params.filterProjectId,
stopCount: params.stopCount,
preventCount: params.preventCount,
stopImmediateCount: params.stopImmediateCount,
touchStopCount: params.touchStopCount,
touchPreventCount: params.touchPreventCount,
touchStopImmediateCount: params.touchStopImmediateCount,
calls: params.calls,
}));
(async () => {
await btn.onclick(ev);
btn.ondblclick(ev);
btn.oncontextmenu(ev);
btn.ontouchstart(touchEv);
btn.ontouchend(touchEv);
console.log(JSON.stringify({
buttonClass: btn.className,
buttonTag: btn.tagName,
buttonText: btn.textContent,
newSession: params.newSession,
filterProjectId: params.filterProjectId,
stopCount: params.stopCount,
preventCount: params.preventCount,
stopImmediateCount: params.stopImmediateCount,
touchStopCount: params.touchStopCount,
touchPreventCount: params.touchPreventCount,
touchStopImmediateCount: params.touchStopImmediateCount,
calls: params.calls,
toasts: params.toasts,
}));
})().catch(err => {
console.error(String(err && err.stack ? err.stack : err));
process.exit(1);
});
"""
def _run_quick_create_case(project_id="example-project"):
def _run_quick_create_case(
project_id="example-project",
*,
active_project="active-project",
fail_new_session=False,
new_session_inflight=None,
):
payload = {
"projectId": project_id,
"activeProject": active_project,
"filterProjectId": active_project,
"calls": [],
"stopCount": 0,
"preventCount": 0,
@@ -296,6 +326,9 @@ def _run_quick_create_case(project_id="example-project"):
"touchStopCount": 0,
"touchPreventCount": 0,
"touchStopImmediateCount": 0,
"failNewSession": fail_new_session,
"newSessionInFlight": new_session_inflight,
"toasts": [],
}
result = subprocess.run(
[NODE, "-e", _HELPER, str(SESSIONS_JS), json.dumps(payload)],
@@ -327,3 +360,30 @@ def test_project_chip_quick_create_keeps_active_filter_and_uses_project_override
assert out["touchStopCount"] >= 2
assert out["touchPreventCount"] == 0
assert out["touchStopImmediateCount"] >= 2
@pytest.mark.skipif(NODE is None, reason="node not on PATH")
def test_project_chip_quick_create_restores_filter_when_new_session_fails():
out = _run_quick_create_case(
"project-123",
active_project="keep-me",
fail_new_session=True,
)
assert out["filterProjectId"] == "keep-me"
assert {"type": "set-filter", "projectId": "project-123"} in out["calls"]
assert {"type": "set-filter", "projectId": "keep-me"} in out["calls"]
assert any(msg.startswith("New conversation failed:") for msg in out["toasts"])
@pytest.mark.skipif(NODE is None, reason="node not on PATH")
def test_project_chip_quick_create_leaves_filter_unchanged_during_inflight_guard():
out = _run_quick_create_case(
"project-123",
active_project="keep-me",
new_session_inflight={"session_id": "existing"},
)
assert out["filterProjectId"] == "keep-me"
assert {"type": "set-filter", "projectId": "project-123"} not in out["calls"]
assert "newSession" not in out