summaryrefslogtreecommitdiffstats
path: root/roles/reverseproxy/files/conversejs/src/plugins/chatview/tests/http-file-upload.js
diff options
context:
space:
mode:
Diffstat (limited to 'roles/reverseproxy/files/conversejs/src/plugins/chatview/tests/http-file-upload.js')
-rw-r--r--roles/reverseproxy/files/conversejs/src/plugins/chatview/tests/http-file-upload.js477
1 files changed, 477 insertions, 0 deletions
diff --git a/roles/reverseproxy/files/conversejs/src/plugins/chatview/tests/http-file-upload.js b/roles/reverseproxy/files/conversejs/src/plugins/chatview/tests/http-file-upload.js
new file mode 100644
index 0000000..8bbd8f2
--- /dev/null
+++ b/roles/reverseproxy/files/conversejs/src/plugins/chatview/tests/http-file-upload.js
@@ -0,0 +1,477 @@
+/*global mock, converse */
+
+const Strophe = converse.env.Strophe;
+const $iq = converse.env.$iq;
+const u = converse.env.utils;
+
+describe("XEP-0363: HTTP File Upload", function () {
+
+ describe("Discovering support", function () {
+
+ it("is done automatically", mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
+ const { api } = _converse;
+ const IQ_stanzas = _converse.connection.IQ_stanzas;
+ await mock.waitUntilDiscoConfirmed(_converse, _converse.bare_jid, [], []);
+ let selector = 'iq[to="montague.lit"] query[xmlns="http://jabber.org/protocol/disco#info"]';
+ let stanza = await u.waitUntil(() => IQ_stanzas.find(iq => iq.querySelector(selector)), 1000);
+
+ /* <iq type='result'
+ * from='plays.shakespeare.lit'
+ * to='romeo@montague.net/orchard'
+ * id='info1'>
+ * <query xmlns='http://jabber.org/protocol/disco#info'>
+ * <identity
+ * category='server'
+ * type='im'/>
+ * <feature var='http://jabber.org/protocol/disco#info'/>
+ * <feature var='http://jabber.org/protocol/disco#items'/>
+ * </query>
+ * </iq>
+ */
+ stanza = $iq({
+ 'type': 'result',
+ 'from': 'montague.lit',
+ 'to': 'romeo@montague.lit/orchard',
+ 'id': stanza.getAttribute('id'),
+ }).c('query', {'xmlns': 'http://jabber.org/protocol/disco#info'})
+ .c('identity', {
+ 'category': 'server',
+ 'type': 'im'}).up()
+ .c('feature', {
+ 'var': 'http://jabber.org/protocol/disco#info'}).up()
+ .c('feature', {
+ 'var': 'http://jabber.org/protocol/disco#items'});
+ _converse.connection._dataRecv(mock.createRequest(stanza));
+
+ // Converse.js sees that the entity has a disco#items feature,
+ // so it will make a query for it.
+ selector = 'iq[to="montague.lit"] query[xmlns="http://jabber.org/protocol/disco#items"]';
+ await u.waitUntil(() => IQ_stanzas.filter(iq => iq.querySelector(selector)).length, 1000);
+ /* <iq from='montague.tld'
+ * id='step_01'
+ * to='romeo@montague.tld/garden'
+ * type='result'>
+ * <query xmlns='http://jabber.org/protocol/disco#items'>
+ * <item jid='upload.montague.tld' name='HTTP File Upload' />
+ * <item jid='conference.montague.tld' name='Chatroom Service' />
+ * </query>
+ * </iq>
+ */
+ selector = 'iq[to="montague.lit"] query[xmlns="http://jabber.org/protocol/disco#items"]';
+ stanza = IQ_stanzas.find(iq => iq.querySelector(selector), 500);
+ stanza = $iq({
+ 'type': 'result',
+ 'from': 'montague.lit',
+ 'to': 'romeo@montague.lit/orchard',
+ 'id': stanza.getAttribute('id'),
+ }).c('query', {'xmlns': 'http://jabber.org/protocol/disco#items'})
+ .c('item', {
+ 'jid': 'upload.montague.lit',
+ 'name': 'HTTP File Upload'});
+
+ _converse.connection._dataRecv(mock.createRequest(stanza));
+
+ let entities = await api.disco.entities.get();
+ expect(entities.length).toBe(3);
+ expect(entities.pluck('jid')).toEqual(['montague.lit', 'romeo@montague.lit', 'upload.montague.lit']);
+
+ expect(entities.get(_converse.domain).features.length).toBe(2);
+ expect(entities.get(_converse.domain).identities.length).toBe(1);
+
+ api.disco.entities.get().then(entities => {
+ expect(entities.length).toBe(3);
+ expect(entities.pluck('jid')).toEqual(['montague.lit', 'romeo@montague.lit', 'upload.montague.lit']);
+ expect(api.disco.entities.items('montague.lit').length).toBe(1);
+ // Converse.js sees that the entity has a disco#info feature, so it will make a query for it.
+ const selector = 'iq[to="upload.montague.lit"] query[xmlns="http://jabber.org/protocol/disco#info"]';
+ return u.waitUntil(() => IQ_stanzas.filter(iq => iq.querySelector(selector)).length > 0);
+ });
+
+ selector = 'iq[to="upload.montague.lit"] query[xmlns="http://jabber.org/protocol/disco#info"]';
+ stanza = await u.waitUntil(() => IQ_stanzas.filter(iq => iq.querySelector(selector)).pop(), 1000);
+ expect(Strophe.serialize(stanza)).toBe(
+ `<iq from="romeo@montague.lit/orchard" id="`+stanza.getAttribute('id')+`" to="upload.montague.lit" type="get" xmlns="jabber:client">`+
+ `<query xmlns="http://jabber.org/protocol/disco#info"/>`+
+ `</iq>`);
+
+ // Upload service responds and reports a maximum file size of 5MiB
+ /* <iq from='upload.montague.tld'
+ * id='step_02'
+ * to='romeo@montague.tld/garden'
+ * type='result'>
+ * <query xmlns='http://jabber.org/protocol/disco#info'>
+ * <identity category='store'
+ * type='file'
+ * name='HTTP File Upload' />
+ * <feature var='urn:xmpp:http:upload:0' />
+ * <x type='result' xmlns='jabber:x:data'>
+ * <field var='FORM_TYPE' type='hidden'>
+ * <value>urn:xmpp:http:upload:0</value>
+ * </field>
+ * <field var='max-file-size'>
+ * <value>5242880</value>
+ * </field>
+ * </x>
+ * </query>
+ * </iq>
+ */
+ stanza = $iq({'type': 'result', 'to': 'romeo@montague.lit/orchard', 'id': stanza.getAttribute('id'), 'from': 'upload.montague.lit'})
+ .c('query', {'xmlns': 'http://jabber.org/protocol/disco#info'})
+ .c('identity', {'category':'store', 'type':'file', 'name':'HTTP File Upload'}).up()
+ .c('feature', {'var':'urn:xmpp:http:upload:0'}).up()
+ .c('x', {'type':'result', 'xmlns':'jabber:x:data'})
+ .c('field', {'var':'FORM_TYPE', 'type':'hidden'})
+ .c('value').t('urn:xmpp:http:upload:0').up().up()
+ .c('field', {'var':'max-file-size'})
+ .c('value').t('5242880');
+ _converse.connection._dataRecv(mock.createRequest(stanza));
+
+ entities = await _converse.api.disco.entities.get();
+ const entity = await api.disco.entities.get('upload.montague.lit');
+ expect(entity.get('parent_jids')).toEqual(['montague.lit']);
+ expect(entity.identities.where({'category': 'store'}).length).toBe(1);
+ const supported = await _converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, _converse.domain);
+ expect(supported).toBe(true);
+ const features = await _converse.api.disco.features.get(Strophe.NS.HTTPUPLOAD, _converse.domain);
+ expect(features.length).toBe(1);
+ expect(features[0].get('jid')).toBe('upload.montague.lit');
+ expect(features[0].dataforms.where({'FORM_TYPE': {value: "urn:xmpp:http:upload:0", type: "hidden"}}).length).toBe(1);
+ }));
+ });
+
+ describe("When not supported", function () {
+ describe("A file upload toolbar button", function () {
+
+ it("does not appear in private chats",
+ mock.initConverse([], {}, async function (_converse) {
+
+ await mock.waitForRoster(_converse, 'current', 3);
+ mock.openControlBox(_converse);
+ const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
+ await mock.openChatBoxFor(_converse, contact_jid);
+ await mock.waitUntilDiscoConfirmed(
+ _converse, _converse.domain,
+ [{'category': 'server', 'type':'IM'}],
+ ['http://jabber.org/protocol/disco#items'], [], 'info');
+
+ await mock.waitUntilDiscoConfirmed(_converse, _converse.domain, [], [], [], 'items');
+ const view = _converse.chatboxviews.get(contact_jid);
+ expect(view.querySelector('.chat-toolbar .fileupload')).toBe(null);
+ }));
+ });
+ });
+
+ describe("When supported", function () {
+
+ describe("A file upload toolbar button", function () {
+
+ it("appears in private chats", mock.initConverse([], {}, async (_converse) => {
+ await mock.waitForRoster(_converse, 'current', 3);
+ const contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@montague.lit';
+ await mock.openChatBoxFor(_converse, contact_jid);
+ const view = _converse.chatboxviews.get(contact_jid);
+
+ await mock.waitUntilDiscoConfirmed(
+ _converse, _converse.domain,
+ [{'category': 'server', 'type':'IM'}],
+ ['http://jabber.org/protocol/disco#items'], [], 'info');
+
+ await mock.waitUntilDiscoConfirmed(_converse, _converse.domain, [], [], ['upload.montague.lit'], 'items')
+ await mock.waitUntilDiscoConfirmed(_converse, 'upload.montague.lit', [], [Strophe.NS.HTTPUPLOAD], []);
+
+ const el = await u.waitUntil(() => view.querySelector('.chat-toolbar .fileupload'));
+ expect(el).not.toEqual(null);
+ }));
+
+ describe("when clicked and a file chosen", function () {
+
+ it("is uploaded and sent out", mock.initConverse(['chatBoxesFetched'], {} ,async (_converse) => {
+ const base_url = 'https://conversejs.org';
+ await mock.waitUntilDiscoConfirmed(
+ _converse, _converse.domain,
+ [{'category': 'server', 'type':'IM'}],
+ ['http://jabber.org/protocol/disco#items'], [], 'info');
+
+ const send_backup = XMLHttpRequest.prototype.send;
+ const IQ_stanzas = _converse.connection.IQ_stanzas;
+
+ await mock.waitUntilDiscoConfirmed(_converse, _converse.domain, [], [], ['upload.montague.tld'], 'items');
+ await mock.waitUntilDiscoConfirmed(_converse, 'upload.montague.tld', [], [Strophe.NS.HTTPUPLOAD], []);
+ await mock.waitForRoster(_converse, 'current');
+ const contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@montague.lit';
+ await mock.openChatBoxFor(_converse, contact_jid);
+ const view = _converse.chatboxviews.get(contact_jid);
+ const file = {
+ 'type': 'image/jpeg',
+ 'size': '23456' ,
+ 'lastModifiedDate': "",
+ 'name': "my-juliet.jpg"
+ };
+ view.model.sendFiles([file]);
+
+ await u.waitUntil(() => IQ_stanzas.filter(iq => iq.querySelector('iq[to="upload.montague.tld"] request')).length);
+ const iq = IQ_stanzas.pop();
+ expect(Strophe.serialize(iq)).toBe(
+ `<iq from="romeo@montague.lit/orchard" `+
+ `id="${iq.getAttribute("id")}" `+
+ `to="upload.montague.tld" `+
+ `type="get" `+
+ `xmlns="jabber:client">`+
+ `<request `+
+ `content-type="image/jpeg" `+
+ `filename="my-juliet.jpg" `+
+ `size="23456" `+
+ `xmlns="urn:xmpp:http:upload:0"/>`+
+ `</iq>`);
+
+ const message = base_url+"/logo/conversejs-filled.svg";
+
+ const stanza = u.toStanza(`
+ <iq from="upload.montague.tld"
+ id="${iq.getAttribute("id")}"
+ to="romeo@montague.lit/orchard"
+ type="result">
+ <slot xmlns="urn:xmpp:http:upload:0">
+ <put url="https://upload.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my-juliet.jpg">
+ <header name="Authorization">Basic Base64String==</header>
+ <header name="Cookie">foo=bar; user=romeo</header>
+ </put>
+ <get url="${message}" />
+ </slot>
+ </iq>`);
+
+ spyOn(XMLHttpRequest.prototype, 'send').and.callFake(async function () {
+ const message = view.model.messages.at(0);
+ const el = await u.waitUntil(() => view.querySelector('.chat-content progress'));
+ expect(el.getAttribute('value')).toBe('0');
+ message.set('progress', 0.5);
+ await u.waitUntil(() => view.querySelector('.chat-content progress').getAttribute('value') === '0.5')
+ message.set('progress', 1);
+ await u.waitUntil(() => view.querySelector('.chat-content progress').getAttribute('value') === '1')
+ message.save({
+ 'upload': _converse.SUCCESS,
+ 'oob_url': message.get('get'),
+ 'body': message.get('get'),
+ });
+ await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length);
+ });
+ let sent_stanza;
+ spyOn(_converse.connection, 'send').and.callFake(stanza => (sent_stanza = stanza));
+ _converse.connection._dataRecv(mock.createRequest(stanza));
+
+ await u.waitUntil(() => sent_stanza, 1000);
+ expect(Strophe.serialize(sent_stanza)).toBe(
+ `<message from="romeo@montague.lit/orchard" `+
+ `id="${sent_stanza.getAttribute("id")}" `+
+ `to="lady.montague@montague.lit" `+
+ `type="chat" `+
+ `xmlns="jabber:client">`+
+ `<body>${message}</body>`+
+ `<active xmlns="http://jabber.org/protocol/chatstates"/>`+
+ `<request xmlns="urn:xmpp:receipts"/>`+
+ `<x xmlns="jabber:x:oob">`+
+ `<url>${message}</url>`+
+ `</x>`+
+ `<origin-id id="${sent_stanza.querySelector('origin-id').getAttribute("id")}" xmlns="urn:xmpp:sid:0"/>`+
+ `</message>`);
+ const img_link_el = await u.waitUntil(() => view.querySelector('converse-chat-message-body .chat-image__link'), 1000);
+ // Check that the image renders
+ expect(img_link_el.outerHTML.replace(/<!-.*?->/g, '').trim()).toEqual(
+ `<a class="chat-image__link" target="_blank" rel="noopener" href="${base_url}/logo/conversejs-filled.svg">`+
+ `<img class="chat-image img-thumbnail" loading="lazy" src="${base_url}/logo/conversejs-filled.svg"></a>`);
+ XMLHttpRequest.prototype.send = send_backup;
+ }));
+
+ it("shows an error message if the file is too large",
+ mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
+
+ const { api } = _converse;
+ const IQ_stanzas = _converse.connection.IQ_stanzas;
+ const IQ_ids = _converse.connection.IQ_ids;
+
+ await mock.waitUntilDiscoConfirmed(_converse, _converse.bare_jid, [], []);
+ await u.waitUntil(() => IQ_stanzas.filter(
+ iq => iq.querySelector('iq[to="montague.lit"] query[xmlns="http://jabber.org/protocol/disco#info"]')).length
+ );
+
+ let stanza = IQ_stanzas.find((iq) =>
+ iq.querySelector('iq[to="montague.lit"] query[xmlns="http://jabber.org/protocol/disco#info"]'));
+
+ const info_IQ_id = IQ_ids[IQ_stanzas.indexOf(stanza)];
+ stanza = $iq({
+ 'type': 'result',
+ 'from': 'montague.lit',
+ 'to': 'romeo@montague.lit/orchard',
+ 'id': info_IQ_id
+ }).c('query', {'xmlns': 'http://jabber.org/protocol/disco#info'})
+ .c('identity', {
+ 'category': 'server',
+ 'type': 'im'}).up()
+ .c('feature', {
+ 'var': 'http://jabber.org/protocol/disco#info'}).up()
+ .c('feature', {
+ 'var': 'http://jabber.org/protocol/disco#items'});
+ _converse.connection._dataRecv(mock.createRequest(stanza));
+
+ await u.waitUntil(function () {
+ // Converse.js sees that the entity has a disco#items feature,
+ // so it will make a query for it.
+ return IQ_stanzas.filter(function (iq) {
+ return iq.querySelector('iq[to="montague.lit"] query[xmlns="http://jabber.org/protocol/disco#items"]');
+ }).length > 0;
+ }, 300);
+
+ stanza = IQ_stanzas.find(function (iq) {
+ return iq.querySelector('iq[to="montague.lit"] query[xmlns="http://jabber.org/protocol/disco#items"]');
+ });
+ const items_IQ_id = IQ_ids[IQ_stanzas.indexOf(stanza)];
+ stanza = $iq({
+ 'type': 'result',
+ 'from': 'montague.lit',
+ 'to': 'romeo@montague.lit/orchard',
+ 'id': items_IQ_id
+ }).c('query', {'xmlns': 'http://jabber.org/protocol/disco#items'})
+ .c('item', {
+ 'jid': 'upload.montague.lit',
+ 'name': 'HTTP File Upload'});
+
+ _converse.connection._dataRecv(mock.createRequest(stanza));
+
+ let entities = await _converse.api.disco.entities.get();
+
+ expect(entities.length).toBe(3);
+ expect(entities.get(_converse.domain).features.length).toBe(2);
+ expect(entities.get(_converse.domain).identities.length).toBe(1);
+ expect(entities.pluck('jid')).toEqual(['montague.lit', 'romeo@montague.lit', 'upload.montague.lit']);
+ expect(api.disco.entities.items('montague.lit').length).toBe(1);
+ await u.waitUntil(function () {
+ // Converse.js sees that the entity has a disco#info feature,
+ // so it will make a query for it.
+ return IQ_stanzas.filter(iq =>
+ iq.querySelector('iq[to="upload.montague.lit"] query[xmlns="http://jabber.org/protocol/disco#info"]')
+ ).length > 0;
+ }, 300);
+
+ stanza = IQ_stanzas.find(iq => iq.querySelector('iq[to="upload.montague.lit"] query[xmlns="http://jabber.org/protocol/disco#info"]'));
+ const IQ_id = IQ_ids[IQ_stanzas.indexOf(stanza)];
+ expect(Strophe.serialize(stanza)).toBe(
+ `<iq from="romeo@montague.lit/orchard" id="${IQ_id}" to="upload.montague.lit" type="get" xmlns="jabber:client">`+
+ `<query xmlns="http://jabber.org/protocol/disco#info"/>`+
+ `</iq>`);
+
+ // Upload service responds and reports a maximum file size of 5MiB
+ stanza = $iq({'type': 'result', 'to': 'romeo@montague.lit/orchard', 'id': IQ_id, 'from': 'upload.montague.lit'})
+ .c('query', {'xmlns': 'http://jabber.org/protocol/disco#info'})
+ .c('identity', {'category':'store', 'type':'file', 'name':'HTTP File Upload'}).up()
+ .c('feature', {'var':'urn:xmpp:http:upload:0'}).up()
+ .c('x', {'type':'result', 'xmlns':'jabber:x:data'})
+ .c('field', {'var':'FORM_TYPE', 'type':'hidden'})
+ .c('value').t('urn:xmpp:http:upload:0').up().up()
+ .c('field', {'var':'max-file-size'})
+ .c('value').t('5242880');
+ _converse.connection._dataRecv(mock.createRequest(stanza));
+ entities = await _converse.api.disco.entities.get();
+ const entity = await api.disco.entities.get('upload.montague.lit');
+ expect(entity.get('parent_jids')).toEqual(['montague.lit']);
+ expect(entity.identities.where({'category': 'store'}).length).toBe(1);
+ await _converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, _converse.domain);
+ await mock.waitForRoster(_converse, 'current');
+
+ const contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@montague.lit';
+ await mock.openChatBoxFor(_converse, contact_jid);
+ const view = _converse.chatboxviews.get(contact_jid);
+ const file = {
+ 'type': 'image/jpeg',
+ 'size': '5242881',
+ 'lastModifiedDate': "",
+ 'name': "my-juliet.jpg"
+ };
+ view.model.sendFiles([file]);
+ await u.waitUntil(() => view.querySelectorAll('.message').length)
+ const messages = view.querySelectorAll('.message.chat-error');
+ expect(messages.length).toBe(1);
+ expect(messages[0].textContent.trim()).toBe(
+ 'The size of your file, my-juliet.jpg, exceeds the maximum allowed by your server, which is 5.24 MB.');
+ }));
+ });
+ });
+
+ describe("While a file is being uploaded", function () {
+
+ it("shows a progress bar", mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
+ await mock.waitUntilDiscoConfirmed(
+ _converse, _converse.domain,
+ [{'category': 'server', 'type':'IM'}],
+ ['http://jabber.org/protocol/disco#items'], [], 'info');
+
+ const IQ_stanzas = _converse.connection.IQ_stanzas;
+
+ await mock.waitUntilDiscoConfirmed(_converse, _converse.domain, [], [], ['upload.montague.tld'], 'items');
+ await mock.waitUntilDiscoConfirmed(_converse, 'upload.montague.tld', [], [Strophe.NS.HTTPUPLOAD], []);
+ await mock.waitForRoster(_converse, 'current');
+ const contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@montague.lit';
+ await mock.openChatBoxFor(_converse, contact_jid);
+ const view = _converse.chatboxviews.get(contact_jid);
+ const file = {
+ 'type': 'image/jpeg',
+ 'size': '23456' ,
+ 'lastModifiedDate': "",
+ 'name': "my-juliet.jpg"
+ };
+ view.model.sendFiles([file]);
+ await u.waitUntil(() => IQ_stanzas.filter(iq => iq.querySelector('iq[to="upload.montague.tld"] request')).length)
+ const iq = IQ_stanzas.pop();
+ expect(Strophe.serialize(iq)).toBe(
+ `<iq from="romeo@montague.lit/orchard" `+
+ `id="${iq.getAttribute("id")}" `+
+ `to="upload.montague.tld" `+
+ `type="get" `+
+ `xmlns="jabber:client">`+
+ `<request `+
+ `content-type="image/jpeg" `+
+ `filename="my-juliet.jpg" `+
+ `size="23456" `+
+ `xmlns="urn:xmpp:http:upload:0"/>`+
+ `</iq>`);
+
+ const base_url = 'https://conversejs.org';
+ const message = base_url+"/logo/conversejs-filled.svg";
+ const stanza = u.toStanza(`
+ <iq from="upload.montague.tld"
+ id="${iq.getAttribute("id")}"
+ to="romeo@montague.lit/orchard"
+ type="result">
+ <slot xmlns="urn:xmpp:http:upload:0">
+ <put url="https://upload.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my-juliet.jpg">
+ <header name="Authorization">Basic Base64String==</header>
+ <header name="Cookie">foo=bar; user=romeo</header>
+ </put>
+ <get url="${message}" />
+ </slot>
+ </iq>`);
+
+ const promise = u.getOpenPromise();
+
+ spyOn(XMLHttpRequest.prototype, 'setRequestHeader');
+ spyOn(XMLHttpRequest.prototype, 'send').and.callFake(async () => {
+ const message = view.model.messages.at(0);
+ const el = await u.waitUntil(() => view.querySelector('.chat-content progress'));
+ expect(el.getAttribute('value')).toBe('0');
+ message.set('progress', 0.5);
+ await u.waitUntil(() => view.querySelector('.chat-content progress').getAttribute('value') === '0.5');
+ message.set('progress', 1);
+ await u.waitUntil(() => view.querySelector('.chat-content progress').getAttribute('value') === '1');
+ expect(view.querySelector('.chat-content .chat-msg__text').textContent).toBe('Uploading file: my-juliet.jpg, 23.46 kB');
+ promise.resolve();
+ });
+ _converse.connection._dataRecv(mock.createRequest(stanza));
+ await promise;
+ expect(XMLHttpRequest.prototype.setRequestHeader.calls.count()).toBe(2);
+ expect(XMLHttpRequest.prototype.setRequestHeader.calls.all()[0].args[0]).toBe('Content-type');
+ expect(XMLHttpRequest.prototype.setRequestHeader.calls.all()[0].args[1]).toBe('image/jpeg');
+ expect(XMLHttpRequest.prototype.setRequestHeader.calls.all()[1].args[0]).toBe('Authorization');
+ expect(XMLHttpRequest.prototype.setRequestHeader.calls.all()[1].args[1]).toBe('Basic Base64String==');
+ }));
+ });
+ });
+});