mirror of
https://github.com/thelounge/thelounge
synced 2024-11-22 12:03:11 +00:00
Allow configuring multiple default networks
This commit is contained in:
commit
55b3eef60e
10 changed files with 626 additions and 493 deletions
|
@ -20,6 +20,20 @@
|
|||
</h1>
|
||||
<template v-if="!config?.lockNetwork">
|
||||
<h2>Network settings</h2>
|
||||
<template v-if="config.defaults.length > 0">
|
||||
<div class="connect-row">
|
||||
<label for="connect:presetName">Preset</label>
|
||||
<select id="connect:presetName" v-model="presetName" name="presetName">
|
||||
<option
|
||||
v-for="defaultNetwork in config.defaults"
|
||||
:key="defaultNetwork.name"
|
||||
:value="defaultNetwork.name"
|
||||
>
|
||||
{{ defaultNetwork.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</template>
|
||||
<div class="connect-row">
|
||||
<label for="connect:name">Name</label>
|
||||
<input
|
||||
|
@ -175,35 +189,48 @@
|
|||
</div>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else-if="config.lockNetwork && !store.state.serverConfiguration?.public">
|
||||
<h2>Network settings</h2>
|
||||
<div class="connect-row">
|
||||
<label for="connect:name">Name</label>
|
||||
<input
|
||||
id="connect:name"
|
||||
v-model.trim="defaults.name"
|
||||
class="input"
|
||||
name="name"
|
||||
maxlength="100"
|
||||
/>
|
||||
</div>
|
||||
<div class="connect-row">
|
||||
<label for="connect:password">Password</label>
|
||||
<RevealPassword
|
||||
v-slot:default="slotProps"
|
||||
class="input-wrap password-container"
|
||||
>
|
||||
<input
|
||||
id="connect:password"
|
||||
v-model="defaults.password"
|
||||
class="input"
|
||||
:type="slotProps.isVisible ? 'text' : 'password'"
|
||||
placeholder="Server password (optional)"
|
||||
name="password"
|
||||
maxlength="300"
|
||||
/>
|
||||
</RevealPassword>
|
||||
</div>
|
||||
<template v-else-if="config.lockNetwork">
|
||||
<template
|
||||
v-if="
|
||||
$store.state.serverConfiguration.defaults.length > 1 ||
|
||||
!$store.state.serverConfiguration.public
|
||||
"
|
||||
>
|
||||
<h2>Network settings</h2>
|
||||
</template>
|
||||
<template v-if="$store.state.serverConfiguration.defaults.length > 1">
|
||||
<div class="connect-row">
|
||||
<label for="connect:name">Network</label>
|
||||
<select id="connect:name" v-model="defaults.name" name="name">
|
||||
<option
|
||||
v-for="defaultNetwork in config.defaults"
|
||||
:key="defaultNetwork.name"
|
||||
:value="defaultNetwork.name"
|
||||
>
|
||||
{{ defaultNetwork.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="!$store.state.serverConfiguration.public">
|
||||
<div class="connect-row">
|
||||
<label for="connect:password">Password</label>
|
||||
<RevealPassword
|
||||
v-slot:default="slotProps"
|
||||
class="input-wrap password-container"
|
||||
>
|
||||
<input
|
||||
id="connect:password"
|
||||
v-model="defaults.password"
|
||||
class="input"
|
||||
:type="slotProps.isVisible ? 'text' : 'password'"
|
||||
placeholder="Server password (optional)"
|
||||
name="password"
|
||||
maxlength="300"
|
||||
/>
|
||||
</RevealPassword>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<h2>User preferences</h2>
|
||||
|
@ -470,6 +497,7 @@ export default defineComponent({
|
|||
const config = ref(store.state.serverConfiguration);
|
||||
const previousUsername = ref(props.defaults?.username);
|
||||
const displayPasswordField = ref(false);
|
||||
const presetName = ref(store.state.serverConfiguration.defaults[0]?.name);
|
||||
|
||||
const publicPassword = ref<HTMLInputElement | null>(null);
|
||||
|
||||
|
@ -481,6 +509,18 @@ export default defineComponent({
|
|||
}
|
||||
});
|
||||
|
||||
watch(presetName, (newValue) => {
|
||||
const defaults = store.state.serverConfiguration.defaults.find(
|
||||
(def) => def.name === newValue
|
||||
);
|
||||
|
||||
if (!defaults) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.assign(props.defaults, defaults);
|
||||
});
|
||||
|
||||
const commandsInput = ref<HTMLInputElement | null>(null);
|
||||
|
||||
const resizeCommandsInput = () => {
|
||||
|
@ -557,6 +597,7 @@ export default defineComponent({
|
|||
store,
|
||||
config,
|
||||
displayPasswordField,
|
||||
presetName,
|
||||
publicPassword,
|
||||
commandsInput,
|
||||
resizeCommandsInput,
|
||||
|
|
|
@ -49,7 +49,7 @@ export default defineComponent({
|
|||
|
||||
if (
|
||||
!Object.prototype.hasOwnProperty.call(
|
||||
store.state.serverConfiguration?.defaults,
|
||||
store.state.serverConfiguration?.defaults[0],
|
||||
key
|
||||
)
|
||||
) {
|
||||
|
@ -57,11 +57,15 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
// When the network is locked, URL overrides should not affect disabled fields
|
||||
if (
|
||||
store.state.serverConfiguration?.lockNetwork &&
|
||||
["name", "host", "port", "tls", "rejectUnauthorized"].includes(key)
|
||||
) {
|
||||
continue;
|
||||
if (store.state.serverConfiguration?.lockNetwork) {
|
||||
if (["host", "port", "tls", "rejectUnauthorized"].includes(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Network name is only disabled if there is a single network
|
||||
if (key === "name" && store.state.serverConfiguration?.defaults.length < 2) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (key === "join") {
|
||||
|
@ -78,7 +82,7 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
// Override server provided defaults with parameters passed in the URL if they match the data type
|
||||
switch (typeof store.state.serverConfiguration?.defaults[key]) {
|
||||
switch (typeof store.state.serverConfiguration?.defaults[0][key]) {
|
||||
case "boolean":
|
||||
if (value === "0" || value === "false") {
|
||||
parsedParams[key] = false;
|
||||
|
@ -102,7 +106,7 @@ export default defineComponent({
|
|||
const defaults = ref<Partial<NetworkFormDefaults>>(
|
||||
Object.assign(
|
||||
{},
|
||||
store.state.serverConfiguration?.defaults,
|
||||
store.state.serverConfiguration?.defaults[0],
|
||||
parseOverrideParams(props.queryParams)
|
||||
)
|
||||
);
|
||||
|
|
|
@ -218,14 +218,14 @@ module.exports = {
|
|||
// default.
|
||||
leaveMessage: "The Lounge - https://thelounge.chat",
|
||||
|
||||
// ## Default network
|
||||
// ## Default networks
|
||||
|
||||
// ### `defaults`
|
||||
//
|
||||
// Specifies default network information that will be used as placeholder
|
||||
// values in the *Connect* window.
|
||||
// Specifies a list of default network information that will be used as
|
||||
// placeholder values in the *Connect* window.
|
||||
//
|
||||
// The available keys for the `defaults` object are:
|
||||
// The available keys for the individual items are:
|
||||
//
|
||||
// - `name`: Name to display in the channel list of The Lounge. This value is
|
||||
// not forwarded to the IRC network.
|
||||
|
@ -249,37 +249,41 @@ module.exports = {
|
|||
// Libera.Chat by default:
|
||||
//
|
||||
// ```js
|
||||
// defaults: {
|
||||
// name: "Libera.Chat",
|
||||
// host: "irc.libera.chat",
|
||||
// port: 6697,
|
||||
// password: "",
|
||||
// tls: true,
|
||||
// rejectUnauthorized: true,
|
||||
// nick: "thelounge%%",
|
||||
// username: "thelounge",
|
||||
// realname: "The Lounge User",
|
||||
// join: "#thelounge"
|
||||
// }
|
||||
// defaults: [
|
||||
// {
|
||||
// name: "Libera.Chat",
|
||||
// host: "irc.libera.chat",
|
||||
// port: 6697,
|
||||
// password: "",
|
||||
// tls: true,
|
||||
// rejectUnauthorized: true,
|
||||
// nick: "thelounge%%",
|
||||
// username: "thelounge",
|
||||
// realname: "The Lounge User",
|
||||
// join: "#thelounge"
|
||||
// },
|
||||
// ]
|
||||
// ```
|
||||
defaults: {
|
||||
name: "Libera.Chat",
|
||||
host: "irc.libera.chat",
|
||||
port: 6697,
|
||||
password: "",
|
||||
tls: true,
|
||||
rejectUnauthorized: true,
|
||||
nick: "thelounge%%",
|
||||
username: "thelounge",
|
||||
realname: "",
|
||||
join: "#thelounge",
|
||||
leaveMessage: "",
|
||||
},
|
||||
defaults: [
|
||||
{
|
||||
name: "Libera.Chat",
|
||||
host: "irc.libera.chat",
|
||||
port: 6697,
|
||||
password: "",
|
||||
tls: true,
|
||||
rejectUnauthorized: true,
|
||||
nick: "thelounge%%",
|
||||
username: "thelounge",
|
||||
realname: "",
|
||||
join: "#thelounge",
|
||||
leaveMessage: "",
|
||||
},
|
||||
],
|
||||
|
||||
// ### `lockNetwork`
|
||||
//
|
||||
// When set to `true`, users will not be able to modify host, port and TLS
|
||||
// settings and will be limited to the configured network.
|
||||
// settings and will be limited to the configured networks.
|
||||
// These fields will also be hidden from the UI.
|
||||
//
|
||||
// This value is set to `false` by default.
|
||||
|
|
|
@ -290,12 +290,15 @@ class Client {
|
|||
});
|
||||
}
|
||||
|
||||
// BUG: this isn't the name of the network, but the username
|
||||
const defaultNetwork =
|
||||
Config.values.defaults.find((network) => this.name === network.name) ||
|
||||
Config.values.defaults[0];
|
||||
|
||||
// TODO; better typing for args
|
||||
const network = new Network({
|
||||
uuid: args.uuid,
|
||||
name: String(
|
||||
args.name || (Config.values.lockNetwork ? Config.values.defaults.name : "") || ""
|
||||
),
|
||||
name: String(args.name || (Config.values.lockNetwork ? defaultNetwork.name : "") || ""),
|
||||
host: String(args.host || ""),
|
||||
port: parseInt(String(args.port), 10),
|
||||
tls: !!args.tls,
|
||||
|
|
|
@ -94,7 +94,7 @@ export type ConfigType = {
|
|||
fileUpload: FileUpload;
|
||||
transports: string[];
|
||||
leaveMessage: string;
|
||||
defaults: Defaults;
|
||||
defaults: Defaults[];
|
||||
lockNetwork: boolean;
|
||||
messageStorage: string[];
|
||||
useHexIp: boolean;
|
||||
|
@ -152,14 +152,16 @@ class Config {
|
|||
return path.join(this.getPackagesPath(), "node_modules", packageName);
|
||||
}
|
||||
|
||||
getDefaultNick() {
|
||||
if (!this.values.defaults.nick) {
|
||||
getDefaultNickForNetwork(networkName: string) {
|
||||
const defaultNick = this.values.defaults.find(
|
||||
(network) => network.name === networkName
|
||||
)?.nick;
|
||||
|
||||
if (!defaultNick) {
|
||||
return "thelounge";
|
||||
}
|
||||
|
||||
return this.values.defaults.nick.replace(/%/g, () =>
|
||||
Math.floor(Math.random() * 10).toString()
|
||||
);
|
||||
return defaultNick.replace(/%/g, () => Math.floor(Math.random() * 10).toString());
|
||||
}
|
||||
|
||||
merge(newConfig: ConfigType) {
|
||||
|
@ -178,17 +180,38 @@ class Config {
|
|||
|
||||
return _.mergeWith(oldConfig, newConfig, (objValue, srcValue, key) => {
|
||||
// Do not override config variables if the type is incorrect (e.g. object changed into a string)
|
||||
// note that Arrays, Objects, Classes et al all have an 'object' type and can't be differentiated
|
||||
if (
|
||||
typeof objValue !== "undefined" &&
|
||||
objValue !== null &&
|
||||
typeof objValue !== typeof srcValue
|
||||
) {
|
||||
log.warn(`Incorrect type for "${colors.bold(key)}", please verify your config.`);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return objValue;
|
||||
}
|
||||
|
||||
if (key === "defaults") {
|
||||
if (srcValue === undefined || srcValue === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
log.warn(
|
||||
`Configuration key "${colors.bold(
|
||||
key
|
||||
)}" should be an array of networks. Support for the old object format will be removed in a future version, please update your config. https://thelounge.chat/docs/configuration#default-networks`
|
||||
);
|
||||
|
||||
// we assume that the user provided value is actually valid... this might be a mistake
|
||||
if (Array.isArray(srcValue)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return srcValue;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return [srcValue];
|
||||
}
|
||||
|
||||
// For arrays, simply override the value with user provided one.
|
||||
if (_.isArray(objValue)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
|
|
|
@ -182,7 +182,7 @@ class Network {
|
|||
// Remove new lines and limit length
|
||||
const cleanString = (str: string) => str.replace(/[\x00\r\n]/g, "").substring(0, 300);
|
||||
|
||||
this.setNick(cleanNick(String(this.nick || Config.getDefaultNick())));
|
||||
this.setNick(cleanNick(String(this.nick || Config.getDefaultNickForNetwork(this.name))));
|
||||
|
||||
if (!this.username) {
|
||||
// If username is empty, make one from the provided nick
|
||||
|
@ -224,27 +224,38 @@ class Network {
|
|||
}
|
||||
|
||||
if (Config.values.lockNetwork) {
|
||||
// Get the first configured network that matches this one, if any.
|
||||
let defaultNetwork = Config.values.defaults.find(
|
||||
(network) => this.name === network.name
|
||||
);
|
||||
|
||||
// BUG: This should probably be an error, not just a silent disregard of the value
|
||||
// Otherwise, default to the first configured
|
||||
if (defaultNetwork === undefined) {
|
||||
defaultNetwork = Config.values.defaults[0];
|
||||
}
|
||||
|
||||
// This check is needed to prevent invalid user configurations
|
||||
if (
|
||||
!Config.values.public &&
|
||||
this.host &&
|
||||
this.host.length > 0 &&
|
||||
this.host !== Config.values.defaults.host
|
||||
defaultNetwork === undefined
|
||||
) {
|
||||
error(this, `The hostname you specified (${this.host}) is not allowed.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Config.values.public) {
|
||||
this.name = Config.values.defaults.name;
|
||||
this.name = defaultNetwork.name;
|
||||
// Sync lobby channel name
|
||||
this.channels[0].name = Config.values.defaults.name;
|
||||
this.channels[0].name = defaultNetwork.name;
|
||||
}
|
||||
|
||||
this.host = Config.values.defaults.host;
|
||||
this.port = Config.values.defaults.port;
|
||||
this.tls = Config.values.defaults.tls;
|
||||
this.rejectUnauthorized = Config.values.defaults.rejectUnauthorized;
|
||||
this.host = defaultNetwork.host;
|
||||
this.port = defaultNetwork.port;
|
||||
this.tls = defaultNetwork.tls;
|
||||
this.rejectUnauthorized = defaultNetwork.rejectUnauthorized;
|
||||
}
|
||||
|
||||
if (this.host.length === 0) {
|
||||
|
|
|
@ -83,7 +83,7 @@ export default <IrcEventHandler>function (irc, network) {
|
|||
lobby.pushMessage(client, msg, true);
|
||||
|
||||
if (irc.connection.registered === false) {
|
||||
irc.changeNick(Config.getDefaultNick());
|
||||
irc.changeNick(Config.getDefaultNickForNetwork(network.name));
|
||||
}
|
||||
|
||||
client.emit("nick", {
|
||||
|
|
|
@ -58,7 +58,6 @@ export type ClientConfiguration = Pick<
|
|||
gitCommit: string | null;
|
||||
defaultTheme: string;
|
||||
themes: ThemeForClient[];
|
||||
defaults: Defaults;
|
||||
fileUploadMaxFileSize?: number;
|
||||
};
|
||||
|
||||
|
@ -872,14 +871,17 @@ function getClientConfiguration(): ClientConfiguration {
|
|||
config.defaults = _.clone(Config.values.defaults);
|
||||
} else {
|
||||
// Only send defaults that are visible on the client
|
||||
config.defaults = _.pick(Config.values.defaults, [
|
||||
"name",
|
||||
"nick",
|
||||
"username",
|
||||
"password",
|
||||
"realname",
|
||||
"join",
|
||||
]) as Defaults;
|
||||
config.defaults = Config.values.defaults.map(
|
||||
(network) =>
|
||||
_.pick(network, [
|
||||
"name",
|
||||
"nick",
|
||||
"username",
|
||||
"password",
|
||||
"realname",
|
||||
"join",
|
||||
]) as Defaults
|
||||
);
|
||||
}
|
||||
|
||||
config.isUpdateAvailable = changelog.isUpdateAvailable;
|
||||
|
@ -888,10 +890,12 @@ function getClientConfiguration(): ClientConfiguration {
|
|||
config.gitCommit = Helper.getGitCommit();
|
||||
config.themes = themes.getAll();
|
||||
config.defaultTheme = Config.values.theme;
|
||||
config.defaults.nick = Config.getDefaultNick();
|
||||
config.defaults.sasl = "";
|
||||
config.defaults.saslAccount = "";
|
||||
config.defaults.saslPassword = "";
|
||||
config.defaults.forEach((network) => {
|
||||
network.nick = Config.getDefaultNickForNetwork(network.name);
|
||||
network.sasl = "";
|
||||
network.saslAccount = "";
|
||||
network.saslPassword = "";
|
||||
});
|
||||
|
||||
if (Uploader) {
|
||||
config.fileUploadMaxFileSize = Uploader.getMaxFileSize();
|
||||
|
|
4
test/fixtures/.thelounge/config.js
vendored
4
test/fixtures/.thelounge/config.js
vendored
|
@ -2,8 +2,8 @@
|
|||
|
||||
import config from "../../../defaults/config.js";
|
||||
|
||||
config.defaults.name = "Example IRC Server";
|
||||
config.defaults.host = "irc.example.com";
|
||||
config.defaults[0].name = "Example IRC Server";
|
||||
config.defaults[0].host = "irc.example.com";
|
||||
config.public = true;
|
||||
config.prefetch = true;
|
||||
// @ts-ignore
|
||||
|
|
|
@ -134,433 +134,476 @@ describe("Network", function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe("#validate()", function () {
|
||||
it("should set correct defaults", function () {
|
||||
Config.values.defaults.nick = "";
|
||||
it("should set correct defaults", function () {
|
||||
Config.values.defaults[0].nick = "";
|
||||
|
||||
const network = new Network({
|
||||
host: "localhost",
|
||||
});
|
||||
|
||||
expect(network.validate({} as any)).to.be.true;
|
||||
expect(network.nick).to.equal("thelounge");
|
||||
expect(network.username).to.equal("thelounge");
|
||||
expect(network.realname).to.equal("thelounge");
|
||||
expect(network.port).to.equal(6667);
|
||||
|
||||
const network2 = new Network({
|
||||
host: "localhost",
|
||||
nick: "@Invalid Nick?",
|
||||
});
|
||||
expect(network2.validate({} as any)).to.be.true;
|
||||
expect(network2.username).to.equal("InvalidNick");
|
||||
const network = new Network({
|
||||
host: "localhost",
|
||||
});
|
||||
|
||||
it("should enforce lockNetwork", function () {
|
||||
Config.values.lockNetwork = true;
|
||||
expect(network.validate({} as any)).to.be.true;
|
||||
expect(network.nick).to.equal("thelounge");
|
||||
expect(network.username).to.equal("thelounge");
|
||||
expect(network.realname).to.equal("thelounge");
|
||||
expect(network.port).to.equal(6667);
|
||||
|
||||
// Make sure we lock in private mode
|
||||
Config.values.public = false;
|
||||
|
||||
const network = new Network({
|
||||
host: "",
|
||||
port: 1337,
|
||||
tls: false,
|
||||
rejectUnauthorized: false,
|
||||
});
|
||||
expect(network.validate({} as any)).to.be.true;
|
||||
expect(network.host).to.equal("irc.example.com");
|
||||
expect(network.port).to.equal(6697);
|
||||
expect(network.tls).to.be.true;
|
||||
expect(network.rejectUnauthorized).to.be.true;
|
||||
|
||||
// Make sure we lock in public mode (also resets public=true for other tests)
|
||||
Config.values.public = true;
|
||||
|
||||
const network2 = new Network({
|
||||
host: "some.fake.tld",
|
||||
});
|
||||
expect(network2.validate({} as any)).to.be.true;
|
||||
expect(network2.host).to.equal("irc.example.com");
|
||||
|
||||
Config.values.lockNetwork = false;
|
||||
});
|
||||
|
||||
it("realname should be set to nick only if realname is empty", function () {
|
||||
const network = new Network({
|
||||
host: "localhost",
|
||||
nick: "dummy",
|
||||
});
|
||||
|
||||
expect(network.validate({} as any)).to.be.true;
|
||||
expect(network.nick).to.equal("dummy");
|
||||
expect(network.realname).to.equal("dummy");
|
||||
|
||||
const network2 = new Network({
|
||||
host: "localhost",
|
||||
nick: "dummy",
|
||||
realname: "notdummy",
|
||||
});
|
||||
|
||||
expect(network2.validate({} as any)).to.be.true;
|
||||
expect(network2.nick).to.equal("dummy");
|
||||
expect(network2.realname).to.equal("notdummy");
|
||||
});
|
||||
|
||||
it("should apply STS policies iff they match", function () {
|
||||
const client = {idMsg: 1, emit() {}} as any;
|
||||
STSPolicies.update("irc.example.com", 7000, 3600);
|
||||
expect(STSPolicies.get("irc.example.com")).to.not.be.null;
|
||||
|
||||
let network = new Network({
|
||||
host: "irc.example.com",
|
||||
port: 1337,
|
||||
tls: false,
|
||||
});
|
||||
|
||||
expect(network.validate(client)).to.be.true;
|
||||
expect(network.port).to.equal(7000);
|
||||
expect(network.tls).to.be.true;
|
||||
|
||||
network = new Network({
|
||||
host: "irc2.example.com",
|
||||
port: 1337,
|
||||
tls: false,
|
||||
});
|
||||
|
||||
expect(network.validate(client)).to.be.true;
|
||||
expect(network.port).to.equal(1337);
|
||||
expect(network.tls).to.be.false;
|
||||
|
||||
STSPolicies.update("irc.example.com", 7000, 0); // Cleanup
|
||||
expect(STSPolicies.get("irc.example.com")).to.be.null;
|
||||
});
|
||||
|
||||
it("should not remove client certs if TLS is disabled", function () {
|
||||
Config.values.public = false;
|
||||
|
||||
const client = {idMsg: 1, emit() {}, messageStorage: []};
|
||||
|
||||
const network = new Network({host: "irc.example.com", sasl: "external"});
|
||||
(network as any).createIrcFramework(client);
|
||||
expect(network.irc).to.not.be.null;
|
||||
|
||||
const client_cert = network.irc?.options?.client_certificate;
|
||||
expect(client_cert).to.not.be.null;
|
||||
expect(ClientCertificate.get(network.uuid)).to.deep.equal(client_cert);
|
||||
|
||||
expect(network.validate(client as any)).to.be.true;
|
||||
|
||||
expect(ClientCertificate.get(network.uuid)).to.deep.equal(client_cert); // Should be unchanged
|
||||
|
||||
ClientCertificate.remove(network.uuid);
|
||||
Config.values.public = true;
|
||||
});
|
||||
|
||||
it("should not remove client certs if there is a STS policy", function () {
|
||||
Config.values.public = false;
|
||||
|
||||
const client = {idMsg: 1, emit() {}, messageStorage: []};
|
||||
STSPolicies.update("irc.example.com", 7000, 3600);
|
||||
expect(STSPolicies.get("irc.example.com")).to.not.be.null;
|
||||
|
||||
const network = new Network({host: "irc.example.com", sasl: "external"});
|
||||
(network as any).createIrcFramework(client);
|
||||
expect(network.irc).to.not.be.null;
|
||||
|
||||
const client_cert = network.irc?.options?.client_certificate;
|
||||
expect(client_cert).to.not.be.null;
|
||||
expect(ClientCertificate.get(network.uuid)).to.deep.equal(client_cert);
|
||||
|
||||
expect(network.validate(client as any)).to.be.true;
|
||||
|
||||
expect(ClientCertificate.get(network.uuid)).to.deep.equal(client_cert); // Should be unchanged
|
||||
|
||||
ClientCertificate.remove(network.uuid);
|
||||
Config.values.public = true;
|
||||
|
||||
STSPolicies.update("irc.example.com", 7000, 0); // Cleanup
|
||||
expect(STSPolicies.get("irc.example.com")).to.be.null;
|
||||
const network2 = new Network({
|
||||
host: "localhost",
|
||||
nick: "@Invalid Nick?",
|
||||
});
|
||||
expect(network2.validate({} as any)).to.be.true;
|
||||
expect(network2.username).to.equal("InvalidNick");
|
||||
});
|
||||
|
||||
describe("#createIrcFramework(client)", function () {
|
||||
it("should generate and use a client certificate when using SASL external", function () {
|
||||
Config.values.public = false;
|
||||
it("should enforce lockNetwork", function () {
|
||||
Config.values.lockNetwork = true;
|
||||
|
||||
const client = {idMsg: 1, emit() {}};
|
||||
STSPolicies.update("irc.example.com", 7000, 3600);
|
||||
expect(STSPolicies.get("irc.example.com")).to.not.be.null;
|
||||
// Make sure we lock in private mode
|
||||
Config.values.public = false;
|
||||
|
||||
let network: any = new Network({host: "irc.example.com"});
|
||||
network.createIrcFramework(client);
|
||||
expect(network.irc).to.not.be.null;
|
||||
expect(network.irc.options.client_certificate).to.be.null;
|
||||
|
||||
network = new Network({host: "irc.example.com", sasl: "external"});
|
||||
network.createIrcFramework(client);
|
||||
expect(network.irc).to.not.be.null;
|
||||
expect(network.irc.options.client_certificate).to.not.be.null;
|
||||
|
||||
ClientCertificate.remove(network.uuid);
|
||||
Config.values.public = true;
|
||||
|
||||
STSPolicies.update("irc.example.com", 7000, 0); // Cleanup
|
||||
expect(STSPolicies.get("irc.example.com")).to.be.null;
|
||||
const network = new Network({
|
||||
host: "",
|
||||
port: 1337,
|
||||
tls: false,
|
||||
rejectUnauthorized: false,
|
||||
});
|
||||
expect(network.validate({} as any)).to.be.true;
|
||||
expect(network.host).to.equal("irc.example.com");
|
||||
expect(network.port).to.equal(6697);
|
||||
expect(network.tls).to.be.true;
|
||||
expect(network.rejectUnauthorized).to.be.true;
|
||||
|
||||
// Make sure we lock in public mode (also resets public=true for other tests)
|
||||
Config.values.public = true;
|
||||
|
||||
const network2 = new Network({
|
||||
host: "some.fake.tld",
|
||||
});
|
||||
expect(network2.validate({} as any)).to.be.true;
|
||||
expect(network2.host).to.equal("irc.example.com");
|
||||
|
||||
Config.values.lockNetwork = false;
|
||||
});
|
||||
|
||||
describe("#edit(client, args)", function () {
|
||||
it("should enforce correct types", function () {
|
||||
let saveCalled = false;
|
||||
let nameEmitCalled = false;
|
||||
|
||||
const network = new Network();
|
||||
(network as any).edit(
|
||||
{
|
||||
emit(name, data) {
|
||||
if (name === "network:name") {
|
||||
nameEmitCalled = true;
|
||||
expect(data.uuid).to.equal(network.uuid);
|
||||
expect(data.name).to.equal("Lounge Test Network");
|
||||
}
|
||||
},
|
||||
save() {
|
||||
saveCalled = true;
|
||||
},
|
||||
},
|
||||
{
|
||||
nick: "newNick",
|
||||
host: "new.tld",
|
||||
name: "Lounge Test Network",
|
||||
port: "1337",
|
||||
tls: undefined,
|
||||
rejectUnauthorized: undefined,
|
||||
username: 1234,
|
||||
password: 4567,
|
||||
realname: 8901,
|
||||
sasl: "something",
|
||||
saslAccount: 1337,
|
||||
saslPassword: 1337,
|
||||
commands: "/command 1 2 3\r\n/ping HELLO\r\r\r\r/whois test\r\n\r\n",
|
||||
ip: "newIp",
|
||||
hostname: "newHostname",
|
||||
uuid: "newuuid",
|
||||
}
|
||||
);
|
||||
|
||||
expect(saveCalled).to.be.true;
|
||||
expect(nameEmitCalled).to.be.true;
|
||||
expect(network.uuid).to.not.equal("newuuid");
|
||||
|
||||
// @ts-expect-error Property 'ip' does not exist on type 'Network'.
|
||||
expect(network.ip).to.be.undefined;
|
||||
// @ts-expect-error Property 'hostname' does not exist on type 'Network'.
|
||||
expect(network.hostname).to.be.undefined;
|
||||
|
||||
expect(network.name).to.equal("Lounge Test Network");
|
||||
expect(network.channels[0].name).to.equal("Lounge Test Network");
|
||||
|
||||
expect(network.nick).to.equal("newNick");
|
||||
expect(network.host).to.equal("new.tld");
|
||||
expect(network.port).to.equal(1337);
|
||||
expect(network.tls).to.be.false;
|
||||
expect(network.rejectUnauthorized).to.be.false;
|
||||
expect(network.username).to.equal("1234");
|
||||
expect(network.password).to.equal("4567");
|
||||
expect(network.realname).to.equal("8901");
|
||||
expect(network.sasl).to.equal("");
|
||||
expect(network.saslAccount).to.equal("1337");
|
||||
expect(network.saslPassword).to.equal("1337");
|
||||
expect(network.commands).to.deep.equal([
|
||||
"/command 1 2 3",
|
||||
"/ping HELLO",
|
||||
"/whois test",
|
||||
]);
|
||||
it("realname should be set to nick only if realname is empty", function () {
|
||||
const network = new Network({
|
||||
host: "localhost",
|
||||
nick: "dummy",
|
||||
});
|
||||
|
||||
expect(network.validate({} as any)).to.be.true;
|
||||
expect(network.nick).to.equal("dummy");
|
||||
expect(network.realname).to.equal("dummy");
|
||||
|
||||
const network2 = new Network({
|
||||
host: "localhost",
|
||||
nick: "dummy",
|
||||
realname: "notdummy",
|
||||
});
|
||||
|
||||
expect(network2.validate({} as any)).to.be.true;
|
||||
expect(network2.nick).to.equal("dummy");
|
||||
expect(network2.realname).to.equal("notdummy");
|
||||
});
|
||||
|
||||
describe("#getFilteredClone(lastActiveChannel, lastMessage)", function () {
|
||||
it("should filter channels", function () {
|
||||
const chan = new Chan();
|
||||
chan.setUser(new User({nick: "test"}));
|
||||
it("should apply STS policies iff they match", function () {
|
||||
const client = {idMsg: 1, emit() {}} as any;
|
||||
STSPolicies.update("irc.example.com", 7000, 3600);
|
||||
expect(STSPolicies.get("irc.example.com")).to.not.be.null;
|
||||
|
||||
const network = new Network({
|
||||
channels: [chan],
|
||||
});
|
||||
|
||||
expect(network.channels[0].users).to.be.empty;
|
||||
let network = new Network({
|
||||
host: "irc.example.com",
|
||||
port: 1337,
|
||||
tls: false,
|
||||
});
|
||||
|
||||
it("should keep necessary properties", function () {
|
||||
const network = new Network();
|
||||
const clone = network.getFilteredClone();
|
||||
expect(network.validate(client)).to.be.true;
|
||||
expect(network.port).to.equal(7000);
|
||||
expect(network.tls).to.be.true;
|
||||
|
||||
expect(clone)
|
||||
.to.be.an("object")
|
||||
.that.has.all.keys("channels", "status", "nick", "name", "serverOptions", "uuid");
|
||||
|
||||
expect(clone.status).to.be.an("object").that.has.all.keys("connected", "secure");
|
||||
network = new Network({
|
||||
host: "irc2.example.com",
|
||||
port: 1337,
|
||||
tls: false,
|
||||
});
|
||||
|
||||
expect(network.validate(client)).to.be.true;
|
||||
expect(network.port).to.equal(1337);
|
||||
expect(network.tls).to.be.false;
|
||||
|
||||
STSPolicies.update("irc.example.com", 7000, 0); // Cleanup
|
||||
expect(STSPolicies.get("irc.example.com")).to.be.null;
|
||||
});
|
||||
|
||||
describe("#addChannel(newChan)", function () {
|
||||
it("should add channel", function () {
|
||||
const chan = new Chan({name: "#thelounge"});
|
||||
it("should not remove client certs if TLS is disabled", function () {
|
||||
Config.values.public = false;
|
||||
|
||||
const network = new Network({
|
||||
channels: [chan],
|
||||
});
|
||||
// Lobby and initial channel
|
||||
expect(network.channels.length).to.equal(2);
|
||||
const client = {idMsg: 1, emit() {}, messageStorage: []};
|
||||
|
||||
const newChan = new Chan({name: "#foo"});
|
||||
network.addChannel(newChan);
|
||||
const network = new Network({host: "irc.example.com", sasl: "external"});
|
||||
(network as any).createIrcFramework(client);
|
||||
expect(network.irc).to.not.be.null;
|
||||
|
||||
expect(network.channels.length).to.equal(3);
|
||||
});
|
||||
const client_cert = network.irc?.options?.client_certificate;
|
||||
expect(client_cert).to.not.be.null;
|
||||
expect(ClientCertificate.get(network.uuid)).to.deep.equal(client_cert);
|
||||
|
||||
it("should add channel alphabetically", function () {
|
||||
const chan1 = new Chan({name: "#abc"});
|
||||
const chan2 = new Chan({name: "#thelounge"});
|
||||
const chan3 = new Chan({name: "#zero"});
|
||||
expect(network.validate(client as any)).to.be.true;
|
||||
|
||||
const network = new Network({
|
||||
channels: [chan1, chan2, chan3],
|
||||
name: "foo",
|
||||
});
|
||||
expect(ClientCertificate.get(network.uuid)).to.deep.equal(client_cert); // Should be unchanged
|
||||
|
||||
const newChan = new Chan({name: "#foo"});
|
||||
network.addChannel(newChan);
|
||||
ClientCertificate.remove(network.uuid);
|
||||
Config.values.public = true;
|
||||
});
|
||||
|
||||
expect(network.channels[0].name).to.equal("foo");
|
||||
expect(network.channels[1]).to.equal(chan1);
|
||||
expect(network.channels[2]).to.equal(newChan);
|
||||
expect(network.channels[3]).to.equal(chan2);
|
||||
expect(network.channels[4]).to.equal(chan3);
|
||||
});
|
||||
it("should not remove client certs if there is a STS policy", function () {
|
||||
Config.values.public = false;
|
||||
|
||||
it("should sort case-insensitively", function () {
|
||||
const chan1 = new Chan({name: "#abc"});
|
||||
const chan2 = new Chan({name: "#THELOUNGE"});
|
||||
const client = {idMsg: 1, emit() {}, messageStorage: []};
|
||||
STSPolicies.update("irc.example.com", 7000, 3600);
|
||||
expect(STSPolicies.get("irc.example.com")).to.not.be.null;
|
||||
|
||||
const network = new Network({
|
||||
channels: [chan1, chan2],
|
||||
});
|
||||
const network = new Network({host: "irc.example.com", sasl: "external"});
|
||||
(network as any).createIrcFramework(client);
|
||||
expect(network.irc).to.not.be.null;
|
||||
|
||||
const newChan = new Chan({name: "#foo"});
|
||||
network.addChannel(newChan);
|
||||
const client_cert = network.irc?.options?.client_certificate;
|
||||
expect(client_cert).to.not.be.null;
|
||||
expect(ClientCertificate.get(network.uuid)).to.deep.equal(client_cert);
|
||||
|
||||
expect(network.channels[1]).to.equal(chan1);
|
||||
expect(network.channels[2]).to.equal(newChan);
|
||||
expect(network.channels[3]).to.equal(chan2);
|
||||
});
|
||||
expect(network.validate(client as any)).to.be.true;
|
||||
|
||||
it("should sort users separately from channels", function () {
|
||||
const chan1 = new Chan({name: "#abc"});
|
||||
const chan2 = new Chan({name: "#THELOUNGE"});
|
||||
expect(ClientCertificate.get(network.uuid)).to.deep.equal(client_cert); // Should be unchanged
|
||||
|
||||
const network = new Network({
|
||||
channels: [chan1, chan2],
|
||||
});
|
||||
ClientCertificate.remove(network.uuid);
|
||||
Config.values.public = true;
|
||||
|
||||
const newUser = new Chan({name: "mcinkay", type: ChanType.QUERY});
|
||||
network.addChannel(newUser);
|
||||
|
||||
expect(network.channels[1]).to.equal(chan1);
|
||||
expect(network.channels[2]).to.equal(chan2);
|
||||
expect(network.channels[3]).to.equal(newUser);
|
||||
});
|
||||
|
||||
it("should sort users alphabetically", function () {
|
||||
const chan1 = new Chan({name: "#abc"});
|
||||
const chan2 = new Chan({name: "#THELOUNGE"});
|
||||
const user1 = new Chan({name: "astorije", type: ChanType.QUERY});
|
||||
const user2 = new Chan({name: "xpaw", type: ChanType.QUERY});
|
||||
|
||||
const network = new Network({
|
||||
channels: [chan1, chan2, user1, user2],
|
||||
});
|
||||
|
||||
const newUser = new Chan({name: "mcinkay", type: ChanType.QUERY});
|
||||
network.addChannel(newUser);
|
||||
|
||||
expect(network.channels[1]).to.equal(chan1);
|
||||
expect(network.channels[2]).to.equal(chan2);
|
||||
expect(network.channels[3]).to.equal(user1);
|
||||
expect(network.channels[4]).to.equal(newUser);
|
||||
expect(network.channels[5]).to.equal(user2);
|
||||
});
|
||||
|
||||
it("should not sort special channels", function () {
|
||||
const chan1 = new Chan({name: "#abc"});
|
||||
const chan2 = new Chan({name: "#THELOUNGE"});
|
||||
const user1 = new Chan({name: "astorije", type: ChanType.QUERY});
|
||||
const user2 = new Chan({name: "xpaw", type: ChanType.QUERY});
|
||||
|
||||
const network = new Network({
|
||||
channels: [chan1, chan2, user1, user2],
|
||||
});
|
||||
|
||||
const newBanlist = new Chan({name: "Banlist for #THELOUNGE", type: ChanType.SPECIAL});
|
||||
network.addChannel(newBanlist);
|
||||
|
||||
expect(network.channels[1]).to.equal(chan1);
|
||||
expect(network.channels[2]).to.equal(chan2);
|
||||
expect(network.channels[3]).to.equal(user1);
|
||||
expect(network.channels[4]).to.equal(user2);
|
||||
expect(network.channels[5]).to.equal(newBanlist);
|
||||
});
|
||||
|
||||
it("should not compare against special channels", function () {
|
||||
const chan1 = new Chan({name: "#abc"});
|
||||
const chan2 = new Chan({name: "#THELOUNGE"});
|
||||
const user1 = new Chan({name: "astorije", type: ChanType.QUERY});
|
||||
|
||||
const network = new Network({
|
||||
channels: [chan1, chan2, user1],
|
||||
});
|
||||
|
||||
const newBanlist = new Chan({name: "Banlist for #THELOUNGE", type: ChanType.SPECIAL});
|
||||
network.addChannel(newBanlist);
|
||||
const newUser = new Chan({name: "mcinkay", type: ChanType.QUERY});
|
||||
network.addChannel(newUser);
|
||||
|
||||
expect(network.channels[1]).to.equal(chan1);
|
||||
expect(network.channels[2]).to.equal(chan2);
|
||||
expect(network.channels[3]).to.equal(user1);
|
||||
expect(network.channels[4]).to.equal(newUser);
|
||||
expect(network.channels[5]).to.equal(newBanlist);
|
||||
});
|
||||
|
||||
it("should insert before first special channel", function () {
|
||||
const banlist = new Chan({name: "Banlist for #THELOUNGE", type: ChanType.SPECIAL});
|
||||
const chan1 = new Chan({name: "#thelounge"});
|
||||
const user1 = new Chan({name: "astorije", type: ChanType.QUERY});
|
||||
|
||||
const network = new Network({
|
||||
channels: [banlist, chan1, user1],
|
||||
});
|
||||
|
||||
const newChan = new Chan({name: "#foo"});
|
||||
network.addChannel(newChan);
|
||||
|
||||
expect(network.channels[1]).to.equal(newChan);
|
||||
expect(network.channels[2]).to.equal(banlist);
|
||||
expect(network.channels[3]).to.equal(chan1);
|
||||
expect(network.channels[4]).to.equal(user1);
|
||||
});
|
||||
|
||||
it("should never add something in front of the lobby", function () {
|
||||
const network = new Network({
|
||||
name: "foo",
|
||||
channels: [],
|
||||
});
|
||||
|
||||
const newUser = new Chan({name: "astorije"});
|
||||
network.addChannel(newUser);
|
||||
|
||||
expect(network.channels[1]).to.equal(newUser);
|
||||
});
|
||||
STSPolicies.update("irc.example.com", 7000, 0); // Cleanup
|
||||
expect(STSPolicies.get("irc.example.com")).to.be.null;
|
||||
});
|
||||
});
|
||||
|
||||
describe("#createIrcFramework(client)", function () {
|
||||
it("should generate and use a client certificate when using SASL external", function () {
|
||||
Config.values.public = false;
|
||||
|
||||
const client = {idMsg: 1, emit() {}};
|
||||
STSPolicies.update("irc.example.com", 7000, 3600);
|
||||
expect(STSPolicies.get("irc.example.com")).to.not.be.null;
|
||||
|
||||
let network: any = new Network({host: "irc.example.com"});
|
||||
network.createIrcFramework(client);
|
||||
expect(network.irc).to.not.be.null;
|
||||
expect(network.irc.options.client_certificate).to.be.null;
|
||||
|
||||
network = new Network({host: "irc.example.com", sasl: "external"});
|
||||
network.createIrcFramework(client);
|
||||
expect(network.irc).to.not.be.null;
|
||||
expect(network.irc.options.client_certificate).to.not.be.null;
|
||||
|
||||
ClientCertificate.remove(network.uuid);
|
||||
Config.values.public = true;
|
||||
|
||||
STSPolicies.update("irc.example.com", 7000, 0); // Cleanup
|
||||
expect(STSPolicies.get("irc.example.com")).to.be.null;
|
||||
});
|
||||
});
|
||||
|
||||
describe("#edit(client, args)", function () {
|
||||
it("lockNetwork should allow networks that are not the first one", function () {
|
||||
Config.values.lockNetwork = true;
|
||||
Config.values.defaults.push({
|
||||
name: "Other Example Network",
|
||||
host: "irc2.example.com",
|
||||
port: 6667,
|
||||
tls: false,
|
||||
rejectUnauthorized: false,
|
||||
password: "",
|
||||
nick: "",
|
||||
username: "",
|
||||
realname: "",
|
||||
leaveMessage: "",
|
||||
sasl: "",
|
||||
saslAccount: "",
|
||||
saslPassword: "",
|
||||
});
|
||||
|
||||
// Make sure we lock in private mode
|
||||
Config.values.public = false;
|
||||
|
||||
const network = new Network({
|
||||
name: "Other Example Network",
|
||||
host: "illegal.example.com",
|
||||
port: 1337,
|
||||
tls: true,
|
||||
rejectUnauthorized: true,
|
||||
});
|
||||
expect(network.validate({} as any)).to.be.true;
|
||||
expect(network.host).to.equal("irc2.example.com");
|
||||
expect(network.port).to.equal(6667);
|
||||
expect(network.tls).to.be.false;
|
||||
expect(network.rejectUnauthorized).to.be.false;
|
||||
|
||||
// Make sure lock in public mode defaults to the first network when
|
||||
// the hostname does not match (also resets public=true and config.defaults
|
||||
// for other tests)
|
||||
Config.values.public = true;
|
||||
|
||||
const network2 = new Network({
|
||||
host: "some.fake.tld",
|
||||
});
|
||||
expect(network2.validate({} as any)).to.be.true;
|
||||
expect(network2.host).to.equal("irc.example.com");
|
||||
|
||||
Config.values.lockNetwork = false;
|
||||
Config.values.defaults.pop();
|
||||
});
|
||||
|
||||
it("should enforce correct types", function () {
|
||||
let saveCalled = false;
|
||||
let nameEmitCalled = false;
|
||||
|
||||
const network = new Network();
|
||||
(network as any).edit(
|
||||
{
|
||||
emit(name, data) {
|
||||
if (name === "network:name") {
|
||||
nameEmitCalled = true;
|
||||
expect(data.uuid).to.equal(network.uuid);
|
||||
expect(data.name).to.equal("Lounge Test Network");
|
||||
}
|
||||
},
|
||||
save() {
|
||||
saveCalled = true;
|
||||
},
|
||||
},
|
||||
{
|
||||
nick: "newNick",
|
||||
host: "new.tld",
|
||||
name: "Lounge Test Network",
|
||||
port: "1337",
|
||||
tls: undefined,
|
||||
rejectUnauthorized: undefined,
|
||||
username: 1234,
|
||||
password: 4567,
|
||||
realname: 8901,
|
||||
sasl: "something",
|
||||
saslAccount: 1337,
|
||||
saslPassword: 1337,
|
||||
commands: "/command 1 2 3\r\n/ping HELLO\r\r\r\r/whois test\r\n\r\n",
|
||||
ip: "newIp",
|
||||
hostname: "newHostname",
|
||||
uuid: "newuuid",
|
||||
}
|
||||
);
|
||||
|
||||
expect(saveCalled).to.be.true;
|
||||
expect(nameEmitCalled).to.be.true;
|
||||
expect(network.uuid).to.not.equal("newuuid");
|
||||
|
||||
// @ts-expect-error Property 'ip' does not exist on type 'Network'.
|
||||
expect(network.ip).to.be.undefined;
|
||||
// @ts-expect-error Property 'hostname' does not exist on type 'Network'.
|
||||
expect(network.hostname).to.be.undefined;
|
||||
|
||||
expect(network.name).to.equal("Lounge Test Network");
|
||||
expect(network.channels[0].name).to.equal("Lounge Test Network");
|
||||
|
||||
expect(network.nick).to.equal("newNick");
|
||||
expect(network.host).to.equal("new.tld");
|
||||
expect(network.port).to.equal(1337);
|
||||
expect(network.tls).to.be.false;
|
||||
expect(network.rejectUnauthorized).to.be.false;
|
||||
expect(network.username).to.equal("1234");
|
||||
expect(network.password).to.equal("4567");
|
||||
expect(network.realname).to.equal("8901");
|
||||
expect(network.sasl).to.equal("");
|
||||
expect(network.saslAccount).to.equal("1337");
|
||||
expect(network.saslPassword).to.equal("1337");
|
||||
expect(network.commands).to.deep.equal(["/command 1 2 3", "/ping HELLO", "/whois test"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#getFilteredClone(lastActiveChannel, lastMessage)", function () {
|
||||
it("should filter channels", function () {
|
||||
const chan = new Chan();
|
||||
chan.setUser(new User({nick: "test"}));
|
||||
|
||||
const network = new Network({
|
||||
channels: [chan],
|
||||
});
|
||||
|
||||
expect(network.channels[0].users).to.be.empty;
|
||||
});
|
||||
|
||||
it("should keep necessary properties", function () {
|
||||
const network = new Network();
|
||||
const clone = network.getFilteredClone();
|
||||
|
||||
expect(clone)
|
||||
.to.be.an("object")
|
||||
.that.has.all.keys("channels", "status", "nick", "name", "serverOptions", "uuid");
|
||||
|
||||
expect(clone.status).to.be.an("object").that.has.all.keys("connected", "secure");
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addChannel(newChan)", function () {
|
||||
it("should add channel", function () {
|
||||
const chan = new Chan({name: "#thelounge"});
|
||||
|
||||
const network = new Network({
|
||||
channels: [chan],
|
||||
});
|
||||
// Lobby and initial channel
|
||||
expect(network.channels.length).to.equal(2);
|
||||
|
||||
const newChan = new Chan({name: "#foo"});
|
||||
network.addChannel(newChan);
|
||||
|
||||
expect(network.channels.length).to.equal(3);
|
||||
});
|
||||
|
||||
it("should add channel alphabetically", function () {
|
||||
const chan1 = new Chan({name: "#abc"});
|
||||
const chan2 = new Chan({name: "#thelounge"});
|
||||
const chan3 = new Chan({name: "#zero"});
|
||||
|
||||
const network = new Network({
|
||||
channels: [chan1, chan2, chan3],
|
||||
name: "foo",
|
||||
});
|
||||
|
||||
const newChan = new Chan({name: "#foo"});
|
||||
network.addChannel(newChan);
|
||||
|
||||
expect(network.channels[0].name).to.equal("foo");
|
||||
expect(network.channels[1]).to.equal(chan1);
|
||||
expect(network.channels[2]).to.equal(newChan);
|
||||
expect(network.channels[3]).to.equal(chan2);
|
||||
expect(network.channels[4]).to.equal(chan3);
|
||||
});
|
||||
|
||||
it("should sort case-insensitively", function () {
|
||||
const chan1 = new Chan({name: "#abc"});
|
||||
const chan2 = new Chan({name: "#THELOUNGE"});
|
||||
|
||||
const network = new Network({
|
||||
channels: [chan1, chan2],
|
||||
});
|
||||
|
||||
const newChan = new Chan({name: "#foo"});
|
||||
network.addChannel(newChan);
|
||||
|
||||
expect(network.channels[1]).to.equal(chan1);
|
||||
expect(network.channels[2]).to.equal(newChan);
|
||||
expect(network.channels[3]).to.equal(chan2);
|
||||
});
|
||||
|
||||
it("should sort users separately from channels", function () {
|
||||
const chan1 = new Chan({name: "#abc"});
|
||||
const chan2 = new Chan({name: "#THELOUNGE"});
|
||||
|
||||
const network = new Network({
|
||||
channels: [chan1, chan2],
|
||||
});
|
||||
|
||||
const newUser = new Chan({name: "mcinkay", type: ChanType.QUERY});
|
||||
network.addChannel(newUser);
|
||||
|
||||
expect(network.channels[1]).to.equal(chan1);
|
||||
expect(network.channels[2]).to.equal(chan2);
|
||||
expect(network.channels[3]).to.equal(newUser);
|
||||
});
|
||||
|
||||
it("should sort users alphabetically", function () {
|
||||
const chan1 = new Chan({name: "#abc"});
|
||||
const chan2 = new Chan({name: "#THELOUNGE"});
|
||||
const user1 = new Chan({name: "astorije", type: ChanType.QUERY});
|
||||
const user2 = new Chan({name: "xpaw", type: ChanType.QUERY});
|
||||
|
||||
const network = new Network({
|
||||
channels: [chan1, chan2, user1, user2],
|
||||
});
|
||||
|
||||
const newUser = new Chan({name: "mcinkay", type: ChanType.QUERY});
|
||||
network.addChannel(newUser);
|
||||
|
||||
expect(network.channels[1]).to.equal(chan1);
|
||||
expect(network.channels[2]).to.equal(chan2);
|
||||
expect(network.channels[3]).to.equal(user1);
|
||||
expect(network.channels[4]).to.equal(newUser);
|
||||
expect(network.channels[5]).to.equal(user2);
|
||||
});
|
||||
|
||||
it("should not sort special channels", function () {
|
||||
const chan1 = new Chan({name: "#abc"});
|
||||
const chan2 = new Chan({name: "#THELOUNGE"});
|
||||
const user1 = new Chan({name: "astorije", type: ChanType.QUERY});
|
||||
const user2 = new Chan({name: "xpaw", type: ChanType.QUERY});
|
||||
|
||||
const network = new Network({
|
||||
channels: [chan1, chan2, user1, user2],
|
||||
});
|
||||
|
||||
const newBanlist = new Chan({name: "Banlist for #THELOUNGE", type: ChanType.SPECIAL});
|
||||
network.addChannel(newBanlist);
|
||||
|
||||
expect(network.channels[1]).to.equal(chan1);
|
||||
expect(network.channels[2]).to.equal(chan2);
|
||||
expect(network.channels[3]).to.equal(user1);
|
||||
expect(network.channels[4]).to.equal(user2);
|
||||
expect(network.channels[5]).to.equal(newBanlist);
|
||||
});
|
||||
|
||||
it("should not compare against special channels", function () {
|
||||
const chan1 = new Chan({name: "#abc"});
|
||||
const chan2 = new Chan({name: "#THELOUNGE"});
|
||||
const user1 = new Chan({name: "astorije", type: ChanType.QUERY});
|
||||
|
||||
const network = new Network({
|
||||
channels: [chan1, chan2, user1],
|
||||
});
|
||||
|
||||
const newBanlist = new Chan({name: "Banlist for #THELOUNGE", type: ChanType.SPECIAL});
|
||||
network.addChannel(newBanlist);
|
||||
const newUser = new Chan({name: "mcinkay", type: ChanType.QUERY});
|
||||
network.addChannel(newUser);
|
||||
|
||||
expect(network.channels[1]).to.equal(chan1);
|
||||
expect(network.channels[2]).to.equal(chan2);
|
||||
expect(network.channels[3]).to.equal(user1);
|
||||
expect(network.channels[4]).to.equal(newUser);
|
||||
expect(network.channels[5]).to.equal(newBanlist);
|
||||
});
|
||||
|
||||
it("should insert before first special channel", function () {
|
||||
const banlist = new Chan({name: "Banlist for #THELOUNGE", type: ChanType.SPECIAL});
|
||||
const chan1 = new Chan({name: "#thelounge"});
|
||||
const user1 = new Chan({name: "astorije", type: ChanType.QUERY});
|
||||
|
||||
const network = new Network({
|
||||
channels: [banlist, chan1, user1],
|
||||
});
|
||||
|
||||
const newChan = new Chan({name: "#foo"});
|
||||
network.addChannel(newChan);
|
||||
|
||||
expect(network.channels[1]).to.equal(newChan);
|
||||
expect(network.channels[2]).to.equal(banlist);
|
||||
expect(network.channels[3]).to.equal(chan1);
|
||||
expect(network.channels[4]).to.equal(user1);
|
||||
});
|
||||
|
||||
it("should never add something in front of the lobby", function () {
|
||||
const network = new Network({
|
||||
name: "foo",
|
||||
channels: [],
|
||||
});
|
||||
|
||||
const newUser = new Chan({name: "astorije"});
|
||||
network.addChannel(newUser);
|
||||
|
||||
expect(network.channels[1]).to.equal(newUser);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue