Skip to content

Commit d9facdc

Browse files
authored
connection: Add tests and documentation for xml:lang stream attribute (#1116)
Add unit tests to verify that the xml:lang functionality works correctly when opening XMPP streams. Document the lang option in the client README. Fixes #1110
1 parent 5099efd commit d9facdc

2 files changed

Lines changed: 76 additions & 0 deletions

File tree

packages/client/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import debug from "@xmpp/debug";
4040
const xmpp = client({
4141
service: "ws://localhost:5280/xmpp-websocket",
4242
domain: "localhost",
43+
lang: "en",
4344
resource: "example",
4445
username: "username",
4546
password: "password",
@@ -100,6 +101,7 @@ See [jid package](/packages/jid)
100101
- `ws://hostname:port/path` plain WebSocket
101102
- `wss://hostname:port/path` secure WebSocket
102103
- `domain` `<string>` Optional domain of the service, if omitted will use the hostname from `service`. Useful when the service domain is different than the service hostname.
104+
- `lang` `<string>` Optional language tag for the stream, sets the `xml:lang` attribute on the stream per [RFC 6120 Section 4.7.4](https://xmpp.org/rfcs/rfc6120.html#streams-attr-xmllang). Useful for servers that use this attribute to determine the language for error messages.
103105
- `resource` `<string`> Optional resource for [resource binding](/packages/resource-binding)
104106
- `username` `<string>` Optional username for [sasl](/packages/sasl)
105107
- `password` `<string>` Optional password for [sasl](/packages/sasl)

packages/connection/test/open.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { EventEmitter } from "@xmpp/events";
2+
import { mockSocket } from "@xmpp/test";
3+
4+
import Connection from "../index.js";
5+
6+
class MockParser extends EventEmitter {}
7+
8+
test("open() sets xml:lang attribute when lang option is provided", async () => {
9+
const conn = new Connection({ domain: "example.com", lang: "en" });
10+
conn.Parser = MockParser;
11+
conn.socket = mockSocket();
12+
13+
// Emit 'open' event after a small delay to let open() set up
14+
setTimeout(() => conn.emit("open"), 10);
15+
16+
await conn.open({ domain: "example.com", lang: "en" });
17+
18+
expect(conn.root.attrs["xml:lang"]).toBe("en");
19+
expect(conn.root.attrs.to).toBe("example.com");
20+
});
21+
22+
test("open() omits xml:lang attribute when lang option is undefined", async () => {
23+
const conn = new Connection({ domain: "example.com" });
24+
conn.Parser = MockParser;
25+
conn.socket = mockSocket();
26+
27+
setTimeout(() => conn.emit("open"), 10);
28+
29+
await conn.open({ domain: "example.com", lang: undefined });
30+
31+
// The xml:lang attribute should be undefined (will be omitted in XML output)
32+
expect(conn.root.attrs["xml:lang"]).toBe(undefined);
33+
expect(conn.root.attrs.to).toBe("example.com");
34+
});
35+
36+
test("start() passes lang option to open()", async () => {
37+
const conn = new Connection({
38+
service: "xmpp://localhost:5222",
39+
domain: "example.com",
40+
lang: "fr",
41+
});
42+
conn.Parser = MockParser;
43+
44+
// Mock connect to succeed immediately
45+
conn.connect = async () => {
46+
conn.socket = mockSocket();
47+
conn._status("connect");
48+
};
49+
50+
// Emit events after start() sets up listeners
51+
setTimeout(() => {
52+
conn.emit("open");
53+
conn.emit("online");
54+
}, 10);
55+
56+
await conn.start();
57+
58+
expect(conn.root.attrs["xml:lang"]).toBe("fr");
59+
});
60+
61+
test("restart() passes lang option to open()", async () => {
62+
const conn = new Connection({
63+
domain: "example.com",
64+
lang: "de",
65+
});
66+
conn.Parser = MockParser;
67+
conn.socket = mockSocket();
68+
69+
setTimeout(() => conn.emit("open"), 10);
70+
71+
await conn.restart();
72+
73+
expect(conn.root.attrs["xml:lang"]).toBe("de");
74+
});

0 commit comments

Comments
 (0)