UNPKG

@converse/headless

Version:

Converse.js Headless build

560 lines (503 loc) 25.3 kB
import mock from '../../../tests/mock.js'; import converse from '../../../dist/converse-headless.js'; const { sizzle, stx, u } = converse.env; describe('A bookmark', function () { it( 'is automatically created when a MUC is entered', mock.initConverse(converse, ['chatBoxesFetched'], {}, async (_converse) => { await mock.waitForRoster(_converse, 'current', 0); await mock.waitUntilBookmarksReturned(_converse); const nick = 'JC'; const muc_jid = 'theplay@conference.shakespeare.lit'; const settings = { name: "Play's the thing", password: 'secret' }; const muc = await mock.openAndEnterMUC(_converse, muc_jid, nick, [], [], true, settings); const IQ_stanzas = _converse.api.connection.get().IQ_stanzas; const sent_stanza = await u.waitUntil(() => IQ_stanzas.filter((s) => sizzle('iq publish[node="urn:xmpp:bookmarks:1"]', s).length).pop(), ); expect(sent_stanza).toEqualStanza( stx`<iq from="${_converse.bare_jid}" to="${_converse.bare_jid}" id="${sent_stanza.getAttribute('id')}" type="set" xmlns="jabber:client"> <pubsub xmlns="http://jabber.org/protocol/pubsub"> <publish node="urn:xmpp:bookmarks:1"> <item id="${muc.get('jid')}"> <conference xmlns="urn:xmpp:bookmarks:1" autojoin="true" name="${settings.name}"> <nick>${nick}</nick> <password>${settings.password}</password> </conference> </item> </publish> <publish-options> <x type="submit" xmlns="jabber:x:data"> <field type="hidden" var="FORM_TYPE"> <value>http://jabber.org/protocol/pubsub#publish-options</value> </field> <field var='pubsub#persist_items'> <value>true</value> </field> <field var='pubsub#max_items'> <value>max</value> </field> <field var='pubsub#send_last_published_item'> <value>never</value> </field> <field var='pubsub#access_model'> <value>whitelist</value> </field> </x> </publish-options> </pubsub> </iq>`, ); const stanza = stx`<iq xmlns="jabber:client" to="${_converse.api.connection.get().jid}" type="result" id="${sent_stanza.getAttribute('id')}"/>`; _converse.api.connection.get()._dataRecv(mock.createRequest(_converse, stanza)); expect(muc.get('bookmarked')).toBeTruthy(); }), ); it( 'will be updated when a user changes their nickname in a MUC', mock.initConverse(converse, [], {}, async function (_converse) { await mock.waitForRoster(_converse, 'current', 0); await mock.waitUntilBookmarksReturned(_converse); const nick = 'JC'; const muc_jid = 'theplay@conference.shakespeare.lit'; const settings = { name: "Play's the thing", password: 'secret' }; const muc = await mock.openAndEnterMUC(_converse, muc_jid, nick, [], [], true, settings); const IQ_stanzas = _converse.api.connection.get().IQ_stanzas; let sent_stanza = await u.waitUntil(() => IQ_stanzas.filter((s) => sizzle('iq publish[node="urn:xmpp:bookmarks:1"]', s).length).pop(), ); const stanza = stx`<iq xmlns="jabber:client" to="${_converse.api.connection.get().jid}" type="result" id="${sent_stanza.getAttribute('id')}"/>`; _converse.api.connection.get()._dataRecv(mock.createRequest(_converse, stanza)); const newnick = 'BAP'; muc.setNickname(newnick); const sent_IQs = _converse.api.connection.get().IQ_stanzas; while (sent_IQs.length) { sent_IQs.pop(); } _converse.api.connection.get()._dataRecv( mock.createRequest(_converse, stx`<presence xmlns="jabber:server" from='${muc_jid}/${nick}' id='DC352437-C019-40EC-B590-AF29E879AF98' to='${_converse.jid}' type='unavailable'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='member' jid='${_converse.jid}' nick='${newnick}' role='participant'/> <status code='303'/> <status code='110'/> </x> </presence>`, ), ); await u.waitUntil(() => muc.get('nick') === newnick); _converse.api.connection.get()._dataRecv( mock.createRequest(_converse, stx`<presence xmlns="jabber:server" from='${muc_jid}/${newnick}' id='5B4F27A4-25ED-43F7-A699-382C6B4AFC67' to='${_converse.jid}'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='member' jid='${_converse.jid}' role='participant'/> <status code='110'/> </x> </presence>`, ), ); sent_stanza = await u.waitUntil(() => IQ_stanzas.filter((s) => sizzle('iq publish[node="urn:xmpp:bookmarks:1"]', s).length).pop(), ); expect(sent_stanza).toEqualStanza( stx`<iq from="${_converse.bare_jid}" to="${_converse.bare_jid}" id="${sent_stanza.getAttribute('id')}" type="set" xmlns="jabber:client"> <pubsub xmlns="http://jabber.org/protocol/pubsub"> <publish node="urn:xmpp:bookmarks:1"> <item id="${muc_jid}"> <conference xmlns="urn:xmpp:bookmarks:1" name="${settings.name}" autojoin="true"> <nick>${newnick}</nick> <password>${settings.password}</password> </conference> </item> </publish> <publish-options> <x type="submit" xmlns="jabber:x:data"> <field type="hidden" var="FORM_TYPE"> <value>http://jabber.org/protocol/pubsub#publish-options</value> </field> <field var='pubsub#persist_items'> <value>true</value> </field> <field var='pubsub#max_items'> <value>max</value> </field> <field var='pubsub#send_last_published_item'> <value>never</value> </field> <field var='pubsub#access_model'> <value>whitelist</value> </field> </x> </publish-options> </pubsub> </iq>`, ); }), ); describe('when autojoin is set', function () { it( 'will cause a MUC to be opened and joined automatically upon login', mock.initConverse(converse, [], {}, async function (_converse) { const { api, state } = _converse; await mock.waitForRoster(_converse, 'current', 0); await mock.waitUntilBookmarksReturned(_converse); spyOn(_converse.api.rooms, 'create').and.callThrough(); const { bookmarks } = _converse.state; let jid = 'theplay@conference.shakespeare.lit'; const model = bookmarks.create({ jid, autojoin: false, name: 'The Play', nick: '', }); expect(_converse.api.rooms.create).not.toHaveBeenCalled(); // Check that we don't auto-join if muc_respect_autojoin is false api.settings.set('muc_respect_autojoin', false); bookmarks.create({ jid, autojoin: true, name: 'The Play', nick: '', }); expect(_converse.api.rooms.create).not.toHaveBeenCalled(); api.settings.set('muc_respect_autojoin', true); bookmarks.remove(model); bookmarks.create({ jid, autojoin: true, name: 'Hamlet', nick: '', }); expect(_converse.api.rooms.create).toHaveBeenCalled(); await mock.waitForMUCDiscoInfo(_converse, jid); await mock.waitForReservedNick(_converse, jid, ''); await u.waitUntil(() => state.chatboxes.length === 1); bookmarks.remove(model); await u.waitUntil(() => state.chatboxes.length === 0); }), ); it( 'has autojoin set to false upon leaving', mock.initConverse(converse, [], {}, async function (_converse) { await mock.waitForRoster(_converse, 'current', 0); await mock.waitUntilBookmarksReturned(_converse); const nick = 'romeo'; const muc_jid = 'theplay@conference.shakespeare.lit'; const settings = { name: 'The Play' }; const muc = await mock.openAndEnterMUC(_converse, muc_jid, nick, [], [], true, settings); const { bookmarks } = _converse.state; await u.waitUntil(() => bookmarks.length); await u.waitUntil(() => muc.get('bookmarked')); spyOn(bookmarks, 'sendBookmarkStanza').and.callThrough(); const sent_IQs = _converse.api.connection.get().IQ_stanzas; while (sent_IQs.length) { sent_IQs.pop(); } await muc.close(); await u.waitUntil(() => sent_IQs.length); // Check that an IQ stanza is sent out, containing no // conferences to bookmark (since we removed the one and // only bookmark). const sent_stanza = sent_IQs.pop(); expect(sent_stanza).toEqualStanza( stx`<iq from="${_converse.bare_jid}" to="${_converse.bare_jid}" id="${sent_stanza.getAttribute('id')}" type="set" xmlns="jabber:client"> <pubsub xmlns="http://jabber.org/protocol/pubsub"> <publish node="urn:xmpp:bookmarks:1"> <item id="${muc_jid}"> <conference xmlns="urn:xmpp:bookmarks:1" name="${settings.name}" autojoin="false"> <nick>${nick}</nick> </conference> </item> </publish> <publish-options> <x type="submit" xmlns="jabber:x:data"> <field type="hidden" var="FORM_TYPE"> <value>http://jabber.org/protocol/pubsub#publish-options</value> </field> <field var='pubsub#persist_items'> <value>true</value> </field> <field var='pubsub#max_items'> <value>max</value> </field> <field var='pubsub#send_last_published_item'> <value>never</value> </field> <field var='pubsub#access_model'> <value>whitelist</value> </field> </x> </publish-options> </pubsub> </iq>`, ); }), ); }); it( 'can be created and sends out a stanza', mock.initConverse(converse, ['connected', 'chatBoxesFetched'], {}, async function (_converse) { await mock.waitForRoster(_converse, 'current', 0); await mock.waitUntilBookmarksReturned(_converse); const bare_jid = _converse.session.get('bare_jid'); const muc1_jid = 'theplay@conference.shakespeare.lit'; const { api } = _converse; await api.bookmarks.set({ jid: muc1_jid, autojoin: true, name: 'Hamlet', nick: '', }); const IQ_stanzas = _converse.api.connection.get().IQ_stanzas; let sent_stanza = await u.waitUntil(() => IQ_stanzas.filter((s) => sizzle('publish[node="urn:xmpp:bookmarks:1"]', s).length).pop(), ); expect(sent_stanza).toEqualStanza(stx` <iq from="${bare_jid}" to="${bare_jid}" id="${sent_stanza.getAttribute('id')}" type="set" xmlns="jabber:client"> <pubsub xmlns="http://jabber.org/protocol/pubsub"> <publish node="urn:xmpp:bookmarks:1"> <item id="${muc1_jid}"> <conference xmlns="urn:xmpp:bookmarks:1" autojoin="true" name="Hamlet"/> </item> </publish> <publish-options> <x type="submit" xmlns="jabber:x:data"> <field type="hidden" var="FORM_TYPE"> <value>http://jabber.org/protocol/pubsub#publish-options</value> </field> <field var='pubsub#persist_items'> <value>true</value> </field> <field var='pubsub#max_items'> <value>max</value> </field> <field var='pubsub#send_last_published_item'> <value>never</value> </field> <field var='pubsub#access_model'> <value>whitelist</value> </field> </x> </publish-options> </pubsub> </iq>`); const muc2_jid = 'balcony@conference.shakespeare.lit'; await api.bookmarks.set({ jid: muc2_jid, autojoin: true, name: 'Balcony', nick: 'romeo', }); sent_stanza = await u.waitUntil(() => IQ_stanzas.filter( (s) => sizzle('publish[node="urn:xmpp:bookmarks:1"] conference[name="Balcony"]', s).length, ).pop(), ); expect(sent_stanza).toEqualStanza(stx` <iq from="${bare_jid}" to="${bare_jid}" id="${sent_stanza.getAttribute('id')}" type="set" xmlns="jabber:client"> <pubsub xmlns="http://jabber.org/protocol/pubsub"> <publish node="urn:xmpp:bookmarks:1"> <item id="${muc2_jid}"> <conference xmlns="urn:xmpp:bookmarks:1" autojoin="true" name="Balcony"> <nick>romeo</nick> </conference> </item> </publish> <publish-options> <x type="submit" xmlns="jabber:x:data"> <field type="hidden" var="FORM_TYPE"> <value>http://jabber.org/protocol/pubsub#publish-options</value> </field> <field var='pubsub#persist_items'> <value>true</value> </field> <field var='pubsub#max_items'> <value>max</value> </field> <field var='pubsub#send_last_published_item'> <value>never</value> </field> <field var='pubsub#access_model'> <value>whitelist</value> </field> </x> </publish-options> </pubsub> </iq>`); const muc3_jid = 'garden@conference.shakespeare.lit'; await api.bookmarks.set({ jid: muc3_jid, autojoin: false, name: 'Garden', nick: 'r0meo', password: 'secret', extensions: [ '<state xmlns="http://myclient.example/bookmark/state" minimized="true"/>', '<levels xmlns="http://myclient.example/bookmark/levels" amount="9000"/>', ], }); sent_stanza = await u.waitUntil(() => IQ_stanzas.filter( (s) => sizzle('publish[node="urn:xmpp:bookmarks:1"] conference[name="Garden"]', s).length, ).pop(), ); expect(sent_stanza).toEqualStanza(stx` <iq xmlns="jabber:client" type="set" from="${bare_jid}" to="${bare_jid}" id="${sent_stanza.getAttribute('id')}"> <pubsub xmlns="http://jabber.org/protocol/pubsub"> <publish node="urn:xmpp:bookmarks:1"> <item id="${muc3_jid}"> <conference xmlns="urn:xmpp:bookmarks:1" autojoin="false" name="Garden"> <nick>r0meo</nick> <password>secret</password> <extensions> <state xmlns="http://myclient.example/bookmark/state" minimized="true"/> <levels xmlns="http://myclient.example/bookmark/levels" amount="9000"/> </extensions> </conference> </item> </publish> <publish-options> <x type="submit" xmlns="jabber:x:data"> <field type="hidden" var="FORM_TYPE"> <value>http://jabber.org/protocol/pubsub#publish-options</value> </field> <field var='pubsub#persist_items'> <value>true</value> </field> <field var='pubsub#max_items'> <value>max</value> </field> <field var='pubsub#send_last_published_item'> <value>never</value> </field> <field var='pubsub#access_model'> <value>whitelist</value> </field> </x> </publish-options> </pubsub> </iq>`); }), ); it( 'handles missing bookmarks gracefully when server responds with item-not-found', mock.initConverse(converse, ['chatBoxesFetched'], {}, async (_converse) => { await mock.waitForRoster(_converse, 'current', 0); await mock.waitUntilDiscoConfirmed( _converse, _converse.bare_jid, [{ 'category': 'pubsub', 'type': 'pep' }], ['http://jabber.org/protocol/pubsub#publish-options', 'urn:xmpp:bookmarks:1#compat'], ); const IQ_stanzas = _converse.api.connection.get().IQ_stanzas; const sent_stanza = await u.waitUntil(() => IQ_stanzas.filter((s) => sizzle(`items[node="urn:xmpp:bookmarks:1"]`, s).length).pop(), ); // Simulate server response with item-not-found error const error_stanza = stx` <iq xmlns="jabber:client" type="error" id="${sent_stanza.getAttribute('id')}" from="${sent_stanza.getAttribute('to')}" to="${sent_stanza.getAttribute('from')}"> <pubsub xmlns="http://jabber.org/protocol/pubsub"> <items node="urn:xmpp:bookmarks:1"/> </pubsub> <error code="404" type="cancel"> <item-not-found xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/> </error> </iq>`; _converse.api.connection.get()._dataRecv(mock.createRequest(_converse, error_stanza)); const { fetched_flag_key } = u.bookmarks.getStorageKeys(); const cache_key = `converse.room-bookmarks.romeo@montague.lit-fetched`; expect(fetched_flag_key).toBe(cache_key); const result = await u.waitUntil(() => _converse.state.session.get(cache_key)); expect(result).toBe(true); }), ); it( 'can be removed and sends out a retract stanza', mock.initConverse(converse, ['connected', 'chatBoxesFetched'], {}, async function (_converse) { await mock.waitForRoster(_converse, 'current', 0); await mock.waitUntilDiscoConfirmed( _converse, _converse.bare_jid, [{ 'category': 'pubsub', 'type': 'pep' }], ['http://jabber.org/protocol/pubsub#publish-options', 'urn:xmpp:bookmarks:1#compat'], ); await mock.waitUntilBookmarksReturned(_converse); const bare_jid = _converse.session.get('bare_jid'); const muc_jid = 'theplay@conference.shakespeare.lit'; const { api } = _converse; // First create a bookmark await api.bookmarks.set({ jid: muc_jid, autojoin: true, name: 'The Play', nick: 'romeo', }); const IQ_stanzas = _converse.api.connection.get().IQ_stanzas; let sent_stanza = await u.waitUntil(() => IQ_stanzas.filter((s) => sizzle('publish[node="urn:xmpp:bookmarks:1"]', s).length).pop(), ); // Server acknowledges successful storage const result_stanza = stx` <iq xmlns="jabber:client" to="${_converse.api.connection.get().jid}" type="result" id="${sent_stanza.getAttribute('id')}"/>`; _converse.api.connection.get()._dataRecv(mock.createRequest(_converse, result_stanza)); // Clear previous stanzas while (IQ_stanzas.length) IQ_stanzas.pop(); // Now remove the bookmark const bookmark = _converse.state.bookmarks.findWhere({ jid: muc_jid }); expect(bookmark).toBeTruthy(); _converse.state.bookmarks.remove(bookmark); // Check that a retract stanza is sent as per XEP-0402 sent_stanza = await u.waitUntil(() => IQ_stanzas.filter((s) => sizzle('retract[node="urn:xmpp:bookmarks:1"]', s).length).pop(), ); expect(sent_stanza).toEqualStanza(stx` <iq from="${bare_jid}" to="${bare_jid}" id="${sent_stanza.getAttribute('id')}" type="set" xmlns="jabber:client"> <pubsub xmlns="http://jabber.org/protocol/pubsub"> <retract node="urn:xmpp:bookmarks:1" notify="true"> <item id="${muc_jid}"/> </retract> </pubsub> </iq>`); }), ); });