From 5bba3a93cddd1c1146de3572a28f6cf0f1cf23d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Tollk=C3=BChn?= Date: Fri, 23 Oct 2026 14:07:28 +0200 Subject: [PATCH] Align invitee separators with the server The server splits the attendee input on whitespace, commas and semicolons, but the search widget only treated commas and semicolons as separators. Typing "a@x.com b" therefore sent the whole string as the search query instead of "b", and picking a contact dropped the whitespace-separated entry. Split on /[\s;,]+/ in both fragment() and commit() so the widget agrees with parseAttendees() on what an entry is. --- js/invitees.js | 6 ++++-- tests/js/invitees.test.mjs | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/js/invitees.js b/js/invitees.js index 802268c..b986f14 100644 --- a/js/invitees.js +++ b/js/invitees.js @@ -13,7 +13,9 @@ 'use strict'; var DEBOUNCE_MS = 250; - var SEPARATOR = /[;,]/; + /* Must match the server's attendee parsing: whitespace, comma and + * semicolon all separate entries. */ + var SEPARATOR = /[\s;,]+/; var input = document.getElementById('atocal-attendees'); @@ -59,7 +61,7 @@ } function commit(email) { - var parts = input.value.split(/([;,])/); + var parts = input.value.split(/([\s;,]+)/); parts.pop(); var prefix = parts.join('').replace(/\s+$/, ''); diff --git a/tests/js/invitees.test.mjs b/tests/js/invitees.test.mjs index b6734f3..bc5ea3b 100644 --- a/tests/js/invitees.test.mjs +++ b/tests/js/invitees.test.mjs @@ -125,4 +125,29 @@ describe('invitee suggestions', () => { expect(list.hidden).toBe(true); expect(list.children).toHaveLength(0); }); + + it('searches only the last whitespace-separated fragment', async () => { + fetch.mockReturnValue(response([BOB])); + + type('alice@example.com bo'); + await settle(); + + expect(fetch).toHaveBeenCalledWith( + SEARCH_URL + '?query=bo', + expect.objectContaining({ headers: { Accept: 'application/json' } }) + ); + }); + + it('keeps whitespace-separated entries when picking a contact', async () => { + fetch.mockReturnValue(response([BOB])); + + type('alice@example.com bo'); + await settle(); + + list.querySelector('button').dispatchEvent( + new MouseEvent('mousedown', { bubbles: true, cancelable: true }) + ); + + expect(input.value).toBe('alice@example.com bob@example.com, '); + }); });