summaryrefslogtreecommitdiffstats
path: root/comm/chat/protocols/xmpp/test
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
commit9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /comm/chat/protocols/xmpp/test
parentInitial commit. (diff)
downloadthunderbird-upstream.tar.xz
thunderbird-upstream.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/chat/protocols/xmpp/test')
-rw-r--r--comm/chat/protocols/xmpp/test/test_authmechs.js160
-rw-r--r--comm/chat/protocols/xmpp/test/test_dnsSrv.js112
-rw-r--r--comm/chat/protocols/xmpp/test/test_parseJidAndNormalization.js104
-rw-r--r--comm/chat/protocols/xmpp/test/test_parseVCard.js139
-rw-r--r--comm/chat/protocols/xmpp/test/test_saslPrep.js66
-rw-r--r--comm/chat/protocols/xmpp/test/test_xmppParser.js135
-rw-r--r--comm/chat/protocols/xmpp/test/test_xmppXml.js103
-rw-r--r--comm/chat/protocols/xmpp/test/xpcshell.ini11
8 files changed, 830 insertions, 0 deletions
diff --git a/comm/chat/protocols/xmpp/test/test_authmechs.js b/comm/chat/protocols/xmpp/test/test_authmechs.js
new file mode 100644
index 0000000000..f935026dbc
--- /dev/null
+++ b/comm/chat/protocols/xmpp/test/test_authmechs.js
@@ -0,0 +1,160 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var { XMPPAuthMechanisms } = ChromeUtils.importESModule(
+ "resource:///modules/xmpp-authmechs.sys.mjs"
+);
+var { Stanza } = ChromeUtils.importESModule(
+ "resource:///modules/xmpp-xml.sys.mjs"
+);
+
+/*
+ * Test PLAIN using the examples given in section 6 of RFC 6120.
+ */
+add_task(async function testPlain() {
+ const username = "juliet";
+ const password = "r0m30myr0m30";
+
+ let mech = XMPPAuthMechanisms.PLAIN(username, password, undefined);
+
+ // Send the initiation message.
+ let result = mech.next();
+ ok(!result.done);
+ let value = await Promise.resolve(result.value);
+
+ // Check the algorithm.
+ equal(value.send.attributes.mechanism, "PLAIN");
+ // Check the PLAIN content.
+ equal(value.send.children[0].text, "AGp1bGlldAByMG0zMG15cjBtMzA=");
+
+ // Receive the success.
+ let response = Stanza.node("success", Stanza.NS.sasl);
+ result = mech.next(response);
+ ok(result.done);
+ // There is no final value.
+ equal(result.value, undefined);
+});
+
+/*
+ * Test SCRAM-SHA-1 using the examples given in section 5 of RFC 5802.
+ *
+ * Full test vectors of intermediate values are available at:
+ * https://wiki.xmpp.org/web/SASL_and_SCRAM-SHA-1
+ */
+add_task(async function testScramSha1() {
+ const username = "user";
+ const password = "pencil";
+
+ // Use a constant value for the nonce.
+ const nonce = "fyko+d2lbbFgONRv9qkxdawL";
+
+ let mech = XMPPAuthMechanisms["SCRAM-SHA-1"](
+ username,
+ password,
+ undefined,
+ nonce
+ );
+
+ // Send the client-first-message.
+ let result = mech.next();
+ ok(!result.done);
+ let value = await Promise.resolve(result.value);
+
+ // Check the algorithm.
+ equal(value.send.attributes.mechanism, "SCRAM-SHA-1");
+ // Check the SCRAM content.
+ equal(
+ atob(value.send.children[0].text),
+ "n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL"
+ );
+
+ // Receive the server-first-message and send the client-final-message.
+ let response = Stanza.node(
+ "challenge",
+ Stanza.NS.sasl,
+ null,
+ btoa(
+ "r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096"
+ )
+ );
+ result = mech.next(response);
+ ok(!result.done);
+ value = await Promise.resolve(result.value);
+
+ // Check the SCRAM content.
+ equal(
+ atob(value.send.children[0].text),
+ "c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts="
+ );
+
+ // Receive the server-final-message.
+ response = Stanza.node(
+ "success",
+ Stanza.NS.sasl,
+ null,
+ btoa("v=rmF9pqV8S7suAoZWja4dJRkFsKQ=")
+ );
+ result = mech.next(response);
+ ok(result.done);
+ // There is no final value.
+ equal(result.value, undefined);
+});
+
+/*
+ * Test SCRAM-SHA-256 using the examples given in section 3 of RFC 7677.
+ */
+add_task(async function testScramSha256() {
+ const username = "user";
+ const password = "pencil";
+
+ // Use a constant value for the nonce.
+ const nonce = "rOprNGfwEbeRWgbNEkqO";
+
+ let mech = XMPPAuthMechanisms["SCRAM-SHA-256"](
+ username,
+ password,
+ undefined,
+ nonce
+ );
+
+ // Send the client-first-message.
+ let result = mech.next();
+ ok(!result.done);
+ let value = await Promise.resolve(result.value);
+
+ // Check the algorithm.
+ equal(value.send.attributes.mechanism, "SCRAM-SHA-256");
+ // Check the SCRAM content.
+ equal(atob(value.send.children[0].text), "n,,n=user,r=rOprNGfwEbeRWgbNEkqO");
+
+ // Receive the server-first-message and send the client-final-message.
+ let response = Stanza.node(
+ "challenge",
+ Stanza.NS.sasl,
+ null,
+ btoa(
+ "r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,s=W22ZaJ0SNY7soEsUEjb6gQ==,i=4096"
+ )
+ );
+ result = mech.next(response);
+ ok(!result.done);
+ value = await Promise.resolve(result.value);
+
+ // Check the SCRAM content.
+ equal(
+ atob(value.send.children[0].text),
+ "c=biws,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,p=dHzbZapWIk4jUhN+Ute9ytag9zjfMHgsqmmiz7AndVQ="
+ );
+
+ // Receive the server-final-message.
+ response = Stanza.node(
+ "success",
+ Stanza.NS.sasl,
+ null,
+ btoa("v=6rriTRBi23WpRR/wtup+mMhUZUn/dB5nLTJRsjl95G4=")
+ );
+ result = mech.next(response);
+ ok(result.done);
+ // There is no final value.
+ equal(result.value, undefined);
+});
diff --git a/comm/chat/protocols/xmpp/test/test_dnsSrv.js b/comm/chat/protocols/xmpp/test/test_dnsSrv.js
new file mode 100644
index 0000000000..37f1b6b052
--- /dev/null
+++ b/comm/chat/protocols/xmpp/test/test_dnsSrv.js
@@ -0,0 +1,112 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var { XMPPAccountPrototype } = ChromeUtils.importESModule(
+ "resource:///modules/xmpp-base.sys.mjs"
+);
+var { XMPPSession } = ChromeUtils.importESModule(
+ "resource:///modules/xmpp-session.sys.mjs"
+);
+var { SRVRecord } = ChromeUtils.import("resource:///modules/DNS.jsm");
+
+function FakeXMPPSession() {}
+FakeXMPPSession.prototype = {
+ __proto__: XMPPSession.prototype,
+ _account: { __proto__: XMPPAccountPrototype },
+ _host: null,
+ _port: 0,
+ connect(
+ aHostOrigin,
+ aPortOrigin,
+ aSecurity,
+ aProxy,
+ aHost = aHostOrigin,
+ aPort = aPortOrigin
+ ) {},
+ _connectNextRecord() {
+ this.isConnectNextRecord = true;
+ },
+
+ // Used to indicate that method _connectNextRecord is called or not.
+ isConnectNextRecord: false,
+
+ LOG(aMsg) {},
+ WARN(aMsg) {},
+};
+
+var TEST_DATA = [
+ {
+ // Test sorting based on priority and weight.
+ input: [
+ new SRVRecord(20, 0, "xmpp.instantbird.com", 5222),
+ new SRVRecord(5, 0, "xmpp1.instantbird.com", 5222),
+ new SRVRecord(10, 0, "xmpp2.instantbird.com", 5222),
+ new SRVRecord(0, 0, "xmpp3.instantbird.com", 5222),
+ new SRVRecord(15, 0, "xmpp4.instantbird.com", 5222),
+ ],
+ output: [
+ new SRVRecord(0, 0, "xmpp3.instantbird.com", 5222),
+ new SRVRecord(5, 0, "xmpp1.instantbird.com", 5222),
+ new SRVRecord(10, 0, "xmpp2.instantbird.com", 5222),
+ new SRVRecord(15, 0, "xmpp4.instantbird.com", 5222),
+ new SRVRecord(20, 0, "xmpp.instantbird.com", 5222),
+ ],
+ isConnectNextRecord: true,
+ },
+ {
+ input: [
+ new SRVRecord(5, 30, "xmpp5.instantbird.com", 5222),
+ new SRVRecord(5, 0, "xmpp1.instantbird.com", 5222),
+ new SRVRecord(10, 60, "xmpp2.instantbird.com", 5222),
+ new SRVRecord(5, 10, "xmpp3.instantbird.com", 5222),
+ new SRVRecord(20, 10, "xmpp.instantbird.com", 5222),
+ new SRVRecord(15, 0, "xmpp4.instantbird.com", 5222),
+ ],
+ output: [
+ new SRVRecord(5, 30, "xmpp5.instantbird.com", 5222),
+ new SRVRecord(5, 10, "xmpp3.instantbird.com", 5222),
+ new SRVRecord(5, 0, "xmpp1.instantbird.com", 5222),
+ new SRVRecord(10, 60, "xmpp2.instantbird.com", 5222),
+ new SRVRecord(15, 0, "xmpp4.instantbird.com", 5222),
+ new SRVRecord(20, 10, "xmpp.instantbird.com", 5222),
+ ],
+ isConnectNextRecord: true,
+ },
+
+ // Tests no SRV records are found.
+ {
+ input: [],
+ output: [],
+ isConnectNextRecord: false,
+ },
+
+ // Tests XMPP is not supported if the result is one record with target ".".
+ {
+ input: [new SRVRecord(5, 30, ".", 5222)],
+ output: XMPPSession.prototype.SRV_ERROR_XMPP_NOT_SUPPORTED,
+ isConnectNextRecord: false,
+ },
+ {
+ input: [new SRVRecord(5, 30, "xmpp.instantbird.com", 5222)],
+ output: [new SRVRecord(5, 30, "xmpp.instantbird.com", 5222)],
+ isConnectNextRecord: true,
+ },
+];
+
+function run_test() {
+ for (let currentQuery of TEST_DATA) {
+ let session = new FakeXMPPSession();
+ try {
+ session._handleSrvQuery(currentQuery.input);
+ equal(session._srvRecords.length, currentQuery.output.length);
+ for (let index = 0; index < session._srvRecords.length; index++) {
+ deepEqual(session._srvRecords[index], currentQuery.output[index]);
+ }
+ } catch (e) {
+ equal(e, currentQuery.output);
+ }
+ equal(session.isConnectNextRecord, currentQuery.isConnectNextRecord);
+ }
+
+ run_next_test();
+}
diff --git a/comm/chat/protocols/xmpp/test/test_parseJidAndNormalization.js b/comm/chat/protocols/xmpp/test/test_parseJidAndNormalization.js
new file mode 100644
index 0000000000..f041d2356b
--- /dev/null
+++ b/comm/chat/protocols/xmpp/test/test_parseJidAndNormalization.js
@@ -0,0 +1,104 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var { XMPPAccountPrototype } = ChromeUtils.importESModule(
+ "resource:///modules/xmpp-base.sys.mjs"
+);
+
+var TEST_DATA = {
+ "abdelrhman@instantbird": {
+ node: "abdelrhman",
+ domain: "instantbird",
+ jid: "abdelrhman@instantbird",
+ normalized: "abdelrhman@instantbird",
+ },
+ " room@instantbird/abdelrhman ": {
+ node: "room",
+ domain: "instantbird",
+ resource: "abdelrhman",
+ jid: "room@instantbird/abdelrhman",
+ normalized: "room@instantbird",
+ },
+ "room@instantbird/@bdelrhman": {
+ node: "room",
+ domain: "instantbird",
+ resource: "@bdelrhman",
+ jid: "room@instantbird/@bdelrhman",
+ normalized: "room@instantbird",
+ },
+ "room@instantbird/abdelrhm\u0061\u0308n": {
+ node: "room",
+ domain: "instantbird",
+ resource: "abdelrhm\u0061\u0308n",
+ jid: "room@instantbird/abdelrhm\u0061\u0308n",
+ normalized: "room@instantbird",
+ },
+ "Room@Instantbird/Abdelrhman": {
+ node: "room",
+ domain: "instantbird",
+ resource: "Abdelrhman",
+ jid: "room@instantbird/Abdelrhman",
+ normalized: "room@instantbird",
+ },
+ "Abdelrhman@instantbird/Instant bird": {
+ node: "abdelrhman",
+ domain: "instantbird",
+ resource: "Instant bird",
+ jid: "abdelrhman@instantbird/Instant bird",
+ normalized: "abdelrhman@instantbird",
+ },
+ "abdelrhman@host/instant/Bird": {
+ node: "abdelrhman",
+ domain: "host",
+ resource: "instant/Bird",
+ jid: "abdelrhman@host/instant/Bird",
+ normalized: "abdelrhman@host",
+ },
+ instantbird: {
+ domain: "instantbird",
+ jid: "instantbird",
+ normalized: "instantbird",
+ },
+};
+
+function testParseJID() {
+ for (let currentJID in TEST_DATA) {
+ let jid = XMPPAccountPrototype._parseJID(currentJID);
+ equal(jid.node, TEST_DATA[currentJID].node);
+ equal(jid.domain, TEST_DATA[currentJID].domain);
+ equal(jid.resource, TEST_DATA[currentJID].resource);
+ equal(jid.jid, TEST_DATA[currentJID].jid);
+ }
+
+ run_next_test();
+}
+
+function testNormalize() {
+ for (let currentJID in TEST_DATA) {
+ equal(
+ XMPPAccountPrototype.normalize(currentJID),
+ TEST_DATA[currentJID].normalized
+ );
+ }
+
+ run_next_test();
+}
+
+function testNormalizeFullJid() {
+ for (let currentJID in TEST_DATA) {
+ equal(
+ XMPPAccountPrototype.normalizeFullJid(currentJID),
+ TEST_DATA[currentJID].jid
+ );
+ }
+
+ run_next_test();
+}
+
+function run_test() {
+ add_test(testParseJID);
+ add_test(testNormalize);
+ add_test(testNormalizeFullJid);
+
+ run_next_test();
+}
diff --git a/comm/chat/protocols/xmpp/test/test_parseVCard.js b/comm/chat/protocols/xmpp/test/test_parseVCard.js
new file mode 100644
index 0000000000..08155218de
--- /dev/null
+++ b/comm/chat/protocols/xmpp/test/test_parseVCard.js
@@ -0,0 +1,139 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var { XMPPAccountPrototype } = ChromeUtils.importESModule(
+ "resource:///modules/xmpp-base.sys.mjs"
+);
+var { XMPPParser } = ChromeUtils.importESModule(
+ "resource:///modules/xmpp-xml.sys.mjs"
+);
+
+/*
+ * Open an input stream, instantiate an XMPP parser, and feed the input string
+ * into it. Then assert that the resulting vCard matches the expected result.
+ */
+function _test_vcard(aInput, aExpectedResult) {
+ let listener = {
+ onXMLError(aError, aException) {
+ // Ensure that no errors happen.
+ ok(false, aError + " - " + aException);
+ },
+ LOG(aString) {},
+ onXmppStanza(aStanza) {
+ // This is a simplified stanza parser that assumes inputs are vCards.
+ let vCard = aStanza.getElement(["vCard"]);
+ deepEqual(XMPPAccountPrototype.parseVCard(vCard), aExpectedResult);
+ },
+ };
+ let parser = new XMPPParser(listener);
+ parser.onDataAvailable(aInput);
+ parser.destroy();
+}
+
+/*
+ * Test parsing of the example vCard from XEP-0054 section 3.1, example 2.
+ */
+function test_standard_vcard() {
+ const standard_vcard =
+ "<iq xmlns='jabber:client'\
+ id='v1'\
+ to='stpeter@jabber.org/roundabout'\
+ type='result'>\
+ <vCard xmlns='vcard-temp'>\
+ <FN>Peter Saint-Andre</FN>\
+ <N>\
+ <FAMILY>Saint-Andre</FAMILY>\
+ <GIVEN>Peter</GIVEN>\
+ <MIDDLE/>\
+ </N>\
+ <NICKNAME>stpeter</NICKNAME>\
+ <URL>http://www.xmpp.org/xsf/people/stpeter.shtml</URL>\
+ <BDAY>1966-08-06</BDAY>\
+ <ORG>\
+ <ORGNAME>XMPP Standards Foundation</ORGNAME>\
+ <ORGUNIT/>\
+ </ORG>\
+ <TITLE>Executive Director</TITLE>\
+ <ROLE>Patron Saint</ROLE>\
+ <TEL><WORK/><VOICE/><NUMBER>303-308-3282</NUMBER></TEL>\
+ <TEL><WORK/><FAX/><NUMBER/></TEL>\
+ <TEL><WORK/><MSG/><NUMBER/></TEL>\
+ <ADR>\
+ <WORK/>\
+ <EXTADD>Suite 600</EXTADD>\
+ <STREET>1899 Wynkoop Street</STREET>\
+ <LOCALITY>Denver</LOCALITY>\
+ <REGION>CO</REGION>\
+ <PCODE>80202</PCODE>\
+ <CTRY>USA</CTRY>\
+ </ADR>\
+ <TEL><HOME/><VOICE/><NUMBER>303-555-1212</NUMBER></TEL>\
+ <TEL><HOME/><FAX/><NUMBER/></TEL>\
+ <TEL><HOME/><MSG/><NUMBER/></TEL>\
+ <ADR>\
+ <HOME/>\
+ <EXTADD/>\
+ <STREET/>\
+ <LOCALITY>Denver</LOCALITY>\
+ <REGION>CO</REGION>\
+ <PCODE>80209</PCODE>\
+ <CTRY>USA</CTRY>\
+ </ADR>\
+ <EMAIL><INTERNET/><PREF/><USERID>stpeter@jabber.org</USERID></EMAIL>\
+ <JABBERID>stpeter@jabber.org</JABBERID>\
+ <DESC>\
+ More information about me is located on my\
+ personal website: http://www.saint-andre.com/\
+ </DESC>\
+ </vCard>\
+</iq>";
+
+ const expectedResult = {
+ fullName: "Peter Saint-Andre",
+ // Name is not parsed.
+ nickname: "stpeter",
+ // URL is not parsed.
+ birthday: "1966-08-06",
+ organization: "XMPP Standards Foundation",
+ title: "Executive Director",
+ // Role is not parsed.
+ // This only pulls the *last* telephone number.
+ telephone: "303-555-1212",
+ // Part of the address is parsed.
+ locality: "Denver",
+ country: "USA",
+ email: "stpeter@jabber.org",
+ userName: "stpeter@jabber.org", // Jabber ID.
+ // Description is not parsed.
+ };
+
+ _test_vcard(standard_vcard, expectedResult);
+
+ run_next_test();
+}
+
+/*
+ * Test parsing of the example empty vCard from XEP-0054 section 3.1, example
+ * 4. This can be used instead of returning an error stanza.
+ */
+function test_empty_vcard() {
+ const empty_vcard =
+ "<iq xmlns='jabber:client'\
+ id='v1'\
+ to='stpeter@jabber.org/roundabout'\
+ type='result'>\
+ <vCard xmlns='vcard-temp'/>\
+</iq>";
+
+ // There should be no properties.
+ _test_vcard(empty_vcard, {});
+
+ run_next_test();
+}
+
+function run_test() {
+ add_test(test_standard_vcard);
+ add_test(test_empty_vcard);
+
+ run_next_test();
+}
diff --git a/comm/chat/protocols/xmpp/test/test_saslPrep.js b/comm/chat/protocols/xmpp/test/test_saslPrep.js
new file mode 100644
index 0000000000..b2d0a1f147
--- /dev/null
+++ b/comm/chat/protocols/xmpp/test/test_saslPrep.js
@@ -0,0 +1,66 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var { saslPrep } = ChromeUtils.importESModule(
+ "resource:///modules/xmpp-authmechs.sys.mjs"
+);
+
+// RFC 4013 3.Examples
+var TEST_DATA = [
+ {
+ // SOFT HYPHEN mapped to nothing.
+ input: "I\u00adX",
+ output: "IX",
+ isError: false,
+ },
+ {
+ // No transformation.
+ input: "user",
+ output: "user",
+ isError: false,
+ },
+ {
+ // Case preserved, will not match #2.
+ input: "USER",
+ output: "USER",
+ isError: false,
+ },
+ {
+ // Output is NFKC, input in ISO 8859-1.
+ input: "\u00aa",
+ output: "a",
+ isError: false,
+ },
+ {
+ // Output is NFKC, will match #1.
+ input: "\u2168",
+ output: "IX",
+ isError: false,
+ },
+ {
+ // Error - prohibited character.
+ input: "\u0007",
+ output: "",
+ isError: true,
+ },
+ {
+ // Error - bidirectional check.
+ input: "\u0627\u0031",
+ output: "",
+ isError: true,
+ },
+];
+
+function run_test() {
+ for (let current of TEST_DATA) {
+ try {
+ let result = saslPrep(current.input);
+ equal(current.isError, false);
+ equal(result, current.output);
+ } catch (e) {
+ equal(current.isError, true);
+ }
+ }
+
+ run_next_test();
+}
diff --git a/comm/chat/protocols/xmpp/test/test_xmppParser.js b/comm/chat/protocols/xmpp/test/test_xmppParser.js
new file mode 100644
index 0000000000..c18304a544
--- /dev/null
+++ b/comm/chat/protocols/xmpp/test/test_xmppParser.js
@@ -0,0 +1,135 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var { XMPPParser } = ChromeUtils.importESModule(
+ "resource:///modules/xmpp-xml.sys.mjs"
+);
+
+let expectedResult =
+ '<presence xmlns="jabber:client" from="chat@example.com/Étienne" to="user@example.com/Thunderbird" \
+xml:lang="en" id="5ed0ae8b7051fa6169037da4e2a1ded6"><c xmlns="http://jabber.org/protocol/caps" \
+ver="ZyB1liM9c9GvKOnvl61+5ScWcqw=" node="https://example.com" hash="sha-1"/><x \
+xmlns="vcard-temp:x:update"><photo xmlns="vcard-temp:x:update"/></x><idle xmlns="urn:xmpp:idle:1" \
+since="2021-04-13T11:52:16.538713+00:00"/><occupant-id xmlns="urn:xmpp:occupant-id:0" \
+id="wNZPCZIVQ51D/heZQpOHi0ZgHXAEQonNPaLdyzLxHWs="/><x xmlns="http://jabber.org/protocol/muc#user"><item \
+xmlns="http://jabber.org/protocol/muc#user" jid="example@example.com/client" affiliation="member" \
+role="participant"/></x></presence>';
+let byteVersion = new TextEncoder().encode(expectedResult);
+let utf8Input = Array.from(byteVersion, byte => String.fromCharCode(byte)).join(
+ ""
+);
+
+var TEST_DATA = [
+ {
+ input:
+ '<message xmlns="jabber:client" from="juliet@capulet.example/balcony" \
+to="romeo@montague.example/garden" type="chat">\
+<body>What man art thou that, thus bescreen"d in night, so stumblest on my \
+counsel?</body>\
+</message>',
+ output:
+ '<message xmlns="jabber:client" \
+from="juliet@capulet.example/balcony" to="romeo@montague.example/garden" \
+type="chat"><body xmlns="jabber:client">What man art thou that, thus \
+bescreen"d in night, so stumblest on my counsel?</body>\
+</message>',
+ isError: false,
+ description: "Message stanza with body element",
+ },
+ {
+ input:
+ '<message xmlns="jabber:client" from="romeo@montague.example" \
+to="romeo@montague.example/home" type="chat">\
+<received xmlns="urn:xmpp:carbons:2">\
+<forwarded xmlns="urn:xmpp:forward:0">\
+<message xmlns="jabber:client" from="juliet@capulet.example/balcony" \
+to="romeo@montague.example/garden" type="chat">\
+<body>What man art thou that, thus bescreen"d in night, so stumblest on my \
+counsel?</body>\
+<thread>0e3141cd80894871a68e6fe6b1ec56fa</thread>\
+</message>\
+</forwarded>\
+</received>\
+</message>',
+ output:
+ '<message xmlns="jabber:client" from="romeo@montague.example" \
+to="romeo@montague.example/home" type="chat">\
+<received xmlns="urn:xmpp:carbons:2"><forwarded xmlns="urn:xmpp:forward:0">\
+<message xmlns="jabber:client" from="juliet@capulet.example/balcony" \
+to="romeo@montague.example/garden" type="chat">\
+<body xmlns="jabber:client">What man art thou that, thus bescreen"d in night, \
+so stumblest on my counsel?</body>\
+<thread xmlns="jabber:client">0e3141cd80894871a68e6fe6b1ec56fa</thread>\
+</message>\
+</forwarded>\
+</received>\
+</message>',
+ isError: false,
+ description: "Forwarded copy of message carbons",
+ },
+ {
+ input:
+ '<message xmlns="jabber:client" from="juliet@capulet.example/balcony" \
+to="romeo@montague.example/garden" type="chat">\
+<body>What man art thou that, thus bescreen"d in night, so stumblest on my \
+counsel?\
+</message>',
+ output: "",
+ isError: true,
+ description: "No closing of body tag",
+ },
+ {
+ input:
+ '<message xmlns="http://etherx.jabber.org/streams" from="juliet@capulet.example/balcony" \
+to="romeo@montague.example/garden" type="chat">\
+<body>What man art thou that, thus bescreen"d in night, so stumblest on my \
+counsel?</body>\
+</message>',
+ output: "",
+ isError: true,
+ description: "Invalid namespace of top-level element",
+ },
+ {
+ input:
+ '<field xmlns="jabber:x:data" type="fixed">\
+<value>What man art thou that, thus bescreen"d in night, so stumblest on my \
+counsel?</value>\
+</field>',
+ output: "",
+ isError: true,
+ description: "Invalid top-level element",
+ },
+ {
+ input: utf8Input,
+ output: expectedResult,
+ isError: false,
+ description: "UTF-8 encoded content from socket",
+ },
+];
+
+function testXMPPParser() {
+ for (let current of TEST_DATA) {
+ let listener = {
+ onXMLError(aString) {
+ ok(current.isError, aString + " - " + current.description);
+ },
+ LOG(aString) {},
+ startLegacyAuth() {},
+ onXmppStanza(aStanza) {
+ equal(current.output, aStanza.getXML(), current.description);
+ ok(!current.isError, current.description);
+ },
+ };
+ let parser = new XMPPParser(listener);
+ parser.onDataAvailable(current.input);
+ parser.destroy();
+ }
+
+ run_next_test();
+}
+
+function run_test() {
+ add_test(testXMPPParser);
+
+ run_next_test();
+}
diff --git a/comm/chat/protocols/xmpp/test/test_xmppXml.js b/comm/chat/protocols/xmpp/test/test_xmppXml.js
new file mode 100644
index 0000000000..1b6ea9a175
--- /dev/null
+++ b/comm/chat/protocols/xmpp/test/test_xmppXml.js
@@ -0,0 +1,103 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var { Stanza } = ChromeUtils.importESModule(
+ "resource:///modules/xmpp-xml.sys.mjs"
+);
+
+var TEST_DATA = [
+ {
+ input: {
+ name: "message",
+ namespace: Stanza.NS.client,
+ attributes: {
+ jid: "user@domain",
+ type: null,
+ },
+ data: [],
+ },
+ XmlOutput: '<message xmlns="jabber:client" jid="user@domain"/>',
+ stringOutput: '<message xmlns="jabber:client" jid="user@domain"/>\n',
+ isError: false,
+ description: "Ignore attribute with null value",
+ },
+ {
+ input: {
+ name: "message",
+ namespace: Stanza.NS.client,
+ attributes: {
+ jid: "user@domain",
+ type: undefined,
+ },
+ data: [],
+ },
+ XmlOutput: '<message xmlns="jabber:client" jid="user@domain"/>',
+ stringOutput: '<message xmlns="jabber:client" jid="user@domain"/>\n',
+ isError: false,
+ description: "Ignore attribute with undefined value",
+ },
+ {
+ input: {
+ name: "message",
+ namespace: undefined,
+ attributes: {},
+ data: [],
+ },
+ XmlOutput: "<message/>",
+ stringOutput: "<message/>\n",
+ isError: false,
+ description: "Ignore namespace with undefined value",
+ },
+ {
+ input: {
+ name: undefined,
+ attributes: {},
+ data: [],
+ },
+ XmlOutput: "",
+ stringOutput: "",
+ isError: true,
+ description: "Node must have a name",
+ },
+ {
+ input: {
+ name: "message",
+ attributes: {},
+ data: "test message",
+ },
+ XmlOutput: "<message>test message</message>",
+ stringOutput: "<message>\n test message\n</message>\n",
+ isError: false,
+ description: "Node with text content",
+ },
+];
+
+function testXMLNode() {
+ for (let current of TEST_DATA) {
+ try {
+ let result = Stanza.node(
+ current.input.name,
+ current.input.namespace,
+ current.input.attributes,
+ current.input.data
+ );
+ equal(result.getXML(), current.XmlOutput, current.description);
+ equal(
+ result.convertToString(),
+ current.stringOutput,
+ current.description
+ );
+ equal(current.isError, false);
+ } catch (e) {
+ equal(current.isError, true, current.description);
+ }
+ }
+
+ run_next_test();
+}
+
+function run_test() {
+ add_test(testXMLNode);
+
+ run_next_test();
+}
diff --git a/comm/chat/protocols/xmpp/test/xpcshell.ini b/comm/chat/protocols/xmpp/test/xpcshell.ini
new file mode 100644
index 0000000000..a4cb9534b8
--- /dev/null
+++ b/comm/chat/protocols/xmpp/test/xpcshell.ini
@@ -0,0 +1,11 @@
+[DEFAULT]
+head =
+tail =
+
+[test_authmechs.js]
+[test_dnsSrv.js]
+[test_parseJidAndNormalization.js]
+[test_saslPrep.js]
+[test_parseVCard.js]
+[test_xmppParser.js]
+[test_xmppXml.js]