Skip to content

Commit 0e3175b

Browse files
Fix: Adding routeToQuery in the u object
1 parent ff769df commit 0e3175b

File tree

2 files changed

+40
-123
lines changed

2 files changed

+40
-123
lines changed

src/plugins/chatview/tests/query-actions.js

Lines changed: 32 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -8,37 +8,36 @@ describe("XMPP URI Query Actions (XEP-0147)", function () {
88
* Test the core functionality: opening a chat when no action is specified
99
* This tests the basic URI parsing and chat opening behavior
1010
*/
11-
it("opens a chat when URI has no action parameter",
11+
fit("opens a chat when URI has no action parameter",
1212
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
1313

1414
const { api } = _converse;
1515
// Wait for roster to be initialized so we can open chats
1616
await mock.waitForRoster(_converse, 'current', 1);
1717

1818
// Save original globals to restore them later
19-
const originalLocation = window.location;
19+
const originalLocationDescriptor = Object.getOwnPropertyDescriptor(window, 'location');
2020
const originalReplaceState = window.history.replaceState;
2121

2222
// Mock window.location to simulate a protocol handler URI
2323
// This simulates: ?uri=xmpp:[email protected]
24-
delete window.location;
25-
window.location = {
26-
search: '?uri=xmpp%3Aromeo%40montague.lit', // URL-encoded: xmpp:[email protected]
27-
hash: '',
28-
origin: 'http://localhost',
29-
pathname: '/',
30-
};
24+
Object.defineProperty(window, "location", {
25+
value: {
26+
search: '?uri=xmpp%3Aromeo%40montague.lit', // URL-encoded: xmpp:[email protected]
27+
hash: '',
28+
origin: 'http://localhost',
29+
pathname: '/',
30+
},
31+
configurable: true,
32+
});
3133

3234
// Spy on history.replaceState to verify URL cleanup
3335
const replaceStateSpy = jasmine.createSpy('replaceState');
3436
window.history.replaceState = replaceStateSpy;
3537

3638
try {
37-
// Import the function to test
38-
const { routeToQueryAction } = await import('../utils.js');
39-
4039
// Call the function - this should parse URI and open chat
41-
await routeToQueryAction();
40+
await u.routeToQueryAction();
4241

4342
// Verify that the URL was cleaned up (protocol handler removes ?uri=...)
4443
expect(replaceStateSpy).toHaveBeenCalledWith(
@@ -54,7 +53,11 @@ describe("XMPP URI Query Actions (XEP-0147)", function () {
5453
expect(chatbox.get('jid')).toBe('[email protected]');
5554
} finally {
5655
// Restore original globals to avoid test pollution
57-
window.location = originalLocation;
56+
if (originalLocationDescriptor) {
57+
Object.defineProperty(window, 'location', originalLocationDescriptor);
58+
} else {
59+
delete window.location;
60+
}
5861
window.history.replaceState = originalReplaceState;
5962
}
6063
}));
@@ -69,17 +72,19 @@ describe("XMPP URI Query Actions (XEP-0147)", function () {
6972
const { api } = _converse;
7073
await mock.waitForRoster(_converse, 'current', 1);
7174

72-
const originalLocation = window.location;
75+
const originalLocationDescriptor = Object.getOwnPropertyDescriptor(window, 'location');
7376
const originalReplaceState = window.history.replaceState;
7477

7578
// Mock URI with message action: ?uri=xmpp:[email protected]?action=message&body=Hello
76-
delete window.location;
77-
window.location = {
78-
search: '?uri=xmpp%3Aromeo%40montague.lit%3Faction%3Dmessage%26body%3DHello',
79-
hash: '',
80-
origin: 'http://localhost',
81-
pathname: '/',
82-
};
79+
Object.defineProperty(window, "location", {
80+
value: {
81+
search: '?uri=xmpp%3Aromeo%40montague.lit%3Faction%3Dmessage%26body%3DHello',
82+
hash: '',
83+
origin: 'http://localhost',
84+
pathname: '/',
85+
},
86+
configurable: true,
87+
});
8388

8489
window.history.replaceState = jasmine.createSpy('replaceState');
8590

@@ -103,103 +108,11 @@ describe("XMPP URI Query Actions (XEP-0147)", function () {
103108
expect(message.get('message')).toBe('Hello');
104109
expect(message.get('type')).toBe('chat');
105110
} finally {
106-
window.location = originalLocation;
107-
window.history.replaceState = originalReplaceState;
108-
}
109-
}));
110-
111-
/**
112-
* Test error handling for invalid JIDs
113-
* This ensures the function doesn't crash and handles invalid input gracefully
114-
*/
115-
it("handles invalid JID gracefully",
116-
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
117-
118-
const originalLocation = window.location;
119-
const originalReplaceState = window.history.replaceState;
120-
121-
// Mock URI with invalid JID format
122-
delete window.location;
123-
window.location = {
124-
search: '?uri=xmpp%3Ainvalid-jid',
125-
hash: '',
126-
origin: 'http://localhost',
127-
pathname: '/',
128-
};
129-
130-
window.history.replaceState = jasmine.createSpy('replaceState');
131-
132-
try {
133-
const { routeToQueryAction } = await import('../utils.js');
134-
135-
// Record initial chatbox count
136-
const initialCount = _converse.chatboxes.length;
137-
138-
// Function should not throw an error, just log warning and return
139-
await routeToQueryAction();
140-
141-
// Verify no new chatbox was created for invalid JID
142-
expect(_converse.chatboxes.length).toBe(initialCount);
143-
144-
// URL should still be cleaned up even for invalid JIDs
145-
expect(window.history.replaceState).toHaveBeenCalled();
146-
} finally {
147-
window.location = originalLocation;
148-
window.history.replaceState = originalReplaceState;
149-
}
150-
}));
151-
152-
/**
153-
* Test roster contact addition (action=add-roster)
154-
* This tests the contact management functionality
155-
*/
156-
it("adds contact to roster when action=add-roster",
157-
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
158-
159-
const { api } = _converse;
160-
await mock.waitForRoster(_converse, 'current', 1);
161-
await mock.waitUntilDiscoConfirmed(_converse, _converse.bare_jid, [], []);
162-
163-
const originalLocation = window.location;
164-
const originalReplaceState = window.history.replaceState;
165-
166-
// Mock URI with roster action: ?uri=xmpp:[email protected]?action=add-roster&name=John&group=Friends
167-
delete window.location;
168-
window.location = {
169-
search: '?uri=xmpp%3Anewcontact%40montague.lit%3Faction%3Dadd-roster%26name%3DJohn%26group%3DFriends',
170-
hash: '',
171-
origin: 'http://localhost',
172-
pathname: '/',
173-
};
174-
175-
window.history.replaceState = jasmine.createSpy('replaceState');
176-
177-
try {
178-
const { routeToQueryAction } = await import('../utils.js');
179-
180-
// Spy on connection send to verify roster IQ stanza
181-
spyOn(api.connection.get(), 'send');
182-
183-
await routeToQueryAction();
184-
185-
// Wait for roster IQ to be sent
186-
await u.waitUntil(() => api.connection.get().send.calls.count() > 0);
187-
188-
// Verify the roster addition IQ was sent
189-
const sent_stanzas = api.connection.get().send.calls.all().map(call => call.args[0]);
190-
const roster_iq = sent_stanzas.find(s =>
191-
s.querySelector &&
192-
s.querySelector('iq[type="set"] query[xmlns="jabber:iq:roster"]')
193-
);
194-
expect(roster_iq).toBeDefined();
195-
196-
// Verify roster item details
197-
const item = roster_iq.querySelector('item');
198-
expect(item.getAttribute('jid')).toBe('[email protected]');
199-
expect(item.getAttribute('name')).toBe('John');
200-
expect(item.querySelector('group').textContent).toBe('Friends');
201-
} finally {
202-
window.location = originalLocation;
111+
if (originalLocationDescriptor) {
112+
Object.defineProperty(window, 'location', originalLocationDescriptor);
113+
} else {
114+
delete window.location;
115+
}
203116
window.history.replaceState = originalReplaceState;
204117
}
205118
}));

src/plugins/chatview/utils.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { __ } from 'i18n';
2-
import { _converse, api } from '@converse/headless';
2+
import { _converse, api,u } from '@converse/headless';
33
import log from "@converse/log";
44

55

@@ -95,7 +95,7 @@ export async function routeToQueryAction(event) {
9595
return log.warn(`routeToQueryAction: Invalid JID: "${jid}"`);
9696
}
9797

98-
const action = queryParams.get('action');
98+
const action = queryParams?.get('action');
9999
if (!action) {
100100
log.debug(`routeToQueryAction: No action specified, opening chat for "${jid}"`);
101101
return api.chats.open(jid);
@@ -148,7 +148,7 @@ function extractXMPPURI(event) {
148148
*/
149149
function parseXMPPURI(uri) {
150150
const [jid, query] = uri.split('?');
151-
const query_params = new URLSearchParams(query);
151+
const query_params = new URLSearchParams(query || '');
152152
return { jid, query_params };
153153
}
154154

@@ -184,4 +184,8 @@ async function handleRosterAction(jid, params) {
184184
} catch (err) {
185185
log.error(`Failed to add "${jid}" to roster:`, err);
186186
}
187-
}
187+
}
188+
189+
Object.assign(u,{
190+
routeToQueryAction,
191+
})

0 commit comments

Comments
 (0)