fix joining channels from net form(?), major progress in tests

This commit is contained in:
Max Leiter 2022-05-31 12:46:55 -07:00
parent 16c6bcf0fc
commit cff9209a25
No known key found for this signature in database
GPG key ID: A3512F2F2F17EBDA
55 changed files with 458 additions and 303 deletions

View file

@ -31,6 +31,7 @@
:class="['user-mode', getModeClass(mode as string)]"
>
<template v-if="userSearchInput.length > 0">
<!-- eslint-disable -->
<Username
v-for="user in users"
:key="user.original.nick + '-search'"
@ -39,6 +40,7 @@
:user="(user.original as any)"
v-html="user.string"
/>
<!-- eslint-enable -->
</template>
<template v-else>
<Username

View file

@ -99,8 +99,8 @@ export default defineComponent({
};
onBeforeRouteLeave((to, from, next) => {
console.log("HERE", to, from);
next();
// cancel the navigation if the user is trying to close the image viewer
if (link.value) {
closeViewer();

View file

@ -182,7 +182,6 @@ export default defineComponent({
return;
}
// @ts-ignore
return friendlysize(props.link.maxSize);
});

View file

@ -122,40 +122,6 @@ export default defineComponent({
store.commit("sidebarOpen", state);
};
const onTouchEnd = () => {
if (!touchStartPos.value?.screenX || !touchCurPos.value?.screenX) {
return;
}
const diff = touchCurPos.value.screenX - touchStartPos.value.screenX;
const absDiff = Math.abs(diff);
if (
absDiff > menuWidth.value / 2 ||
(Date.now() - touchStartTime.value < 180 && absDiff > 50)
) {
toggle(diff > 0);
}
document.body.removeEventListener("touchmove", onTouchMove);
document.body.removeEventListener("touchend", onTouchEnd);
store.commit("sidebarDragging", false);
if (sidebar.value) {
sidebar.value.style.transform = "";
}
if (props.overlay) {
props.overlay.style.opacity = "";
}
touchStartPos.value = null;
touchCurPos.value = null;
touchStartTime.value = 0;
menuIsMoving.value = false;
};
const onTouchMove = (e: TouchEvent) => {
const touch = (touchCurPos.value = e.touches.item(0));
@ -176,6 +142,7 @@ export default defineComponent({
// menu must be open; gestures in 45°-90° (>1) are considered vertical, so
// chat windows must be scrolled.
if (Math.abs(distY / distX) >= 1) {
// eslint-disable-next-line no-use-before-define
onTouchEnd();
return;
}
@ -212,6 +179,40 @@ export default defineComponent({
}
};
const onTouchEnd = () => {
if (!touchStartPos.value?.screenX || !touchCurPos.value?.screenX) {
return;
}
const diff = touchCurPos.value.screenX - touchStartPos.value.screenX;
const absDiff = Math.abs(diff);
if (
absDiff > menuWidth.value / 2 ||
(Date.now() - touchStartTime.value < 180 && absDiff > 50)
) {
toggle(diff > 0);
}
document.body.removeEventListener("touchmove", onTouchMove);
document.body.removeEventListener("touchend", onTouchEnd);
store.commit("sidebarDragging", false);
if (sidebar.value) {
sidebar.value.style.transform = "";
}
if (props.overlay) {
props.overlay.style.opacity = "";
}
touchStartPos.value = null;
touchCurPos.value = null;
touchStartTime.value = 0;
menuIsMoving.value = false;
};
const onTouchStart = (e: TouchEvent) => {
if (!sidebar.value) {
return;

View file

@ -26,7 +26,8 @@ export default defineComponent({
name: "Username",
props: {
user: {
type: Object as PropType<UsernameUser>,
// TODO: UserInMessage shouldn't be necessary here.
type: Object as PropType<UsernameUser | UserInMessage>,
required: true,
},
active: Boolean,
@ -46,7 +47,9 @@ export default defineComponent({
return props.user.mode;
});
const nickColor = computed(() => colorClass(props.user.nick));
// TODO: Nick must be ! because our user prop union includes UserInMessage
const nickColor = computed(() => colorClass(props.user.nick!));
const hover = () => {
if (props.onHover) {

View file

@ -4,7 +4,6 @@
<script lang="ts">
import {defineComponent, ref} from "vue";
import {Defaults} from "../../../src/config";
import socket from "../../js/socket";
import {useStore} from "../../js/store";

View file

@ -275,8 +275,6 @@ export default defineComponent({
messages.value.length && !(messages.value.length % 100)
);
console.log("offset", offset.value);
if (!offset.value) {
await jumpToBottom();
} else {

View file

@ -13,6 +13,7 @@ function input(args) {
if (chanTypes && chanTypes.length > 0) {
for (let c = 0; c < channelList.length; c++) {
if (!chanTypes.includes(channelList[c][0])) {
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
channelList[c] = chanTypes[0] + channelList[c];
}
}
@ -26,6 +27,7 @@ function input(args) {
switchToChannel(chan);
} else {
socket.emit("input", {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
text: `/join ${channels} ${args.length > 1 ? args[1] : ""}`,
target: store.state.activeChannel.channel.id,
});

View file

@ -1,9 +1,14 @@
/* eslint-disable @typescript-eslint/restrict-plus-operands */
// Escapes the RegExp special characters "^", "$", "", ".", "*", "+", "?", "(",
// ")", "[", "]", "{", "}", and "|" in string.
// See https://lodash.com/docs/#escapeRegExp
import escapeRegExp from "lodash/escapeRegExp";
import {Part} from "./merge";
export type ChannelPart = Part & {
channel: string;
};
// Given an array of channel prefixes (such as "#" and "&") and an array of user
// modes (such as "@" and "+"), this function extracts channels and nicks from a
// text.
@ -39,8 +44,4 @@ function findChannels(text: string, channelPrefixes: string[], userModes: string
return result;
}
export type ChannelPart = Part & {
channel: string;
};
export default findChannels;

View file

@ -3,6 +3,10 @@ import {Part} from "./merge";
const regExp = emojiRegExp();
export type EmojiPart = Part & {
emoji: string;
};
function findEmoji(text: string) {
const result: EmojiPart[] = [];
let match;
@ -10,6 +14,7 @@ function findEmoji(text: string) {
while ((match = regExp.exec(text))) {
result.push({
start: match.index,
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
end: match.index + match[0].length,
emoji: match[0],
});
@ -18,8 +23,4 @@ function findEmoji(text: string) {
return result;
}
export type EmojiPart = Part & {
emoji: string;
};
export default findEmoji;

View file

@ -24,8 +24,6 @@
"module": "es2015",
"moduleResolution": "node",
// TODO: Remove eventually, this is due to typescript checking vue files that don't have lang="ts".
"checkJs": false,
// TODO: Remove eventually
"noImplicitAny": false /*Enable error reporting for expressions and declarations with an implied any type. See more: https://www.typescriptlang.org/tsconfig#noImplicitAny */
} /* Instructs the TypeScript compiler how to compile .ts files. */

View file

@ -24,7 +24,7 @@
"lint:stylelint": "stylelint --color \"client/**/*.css\"",
"start": "node src/dist/src/index start",
"test": "run-p --aggregate-output --continue-on-error lint:* test:*",
"test:mocha": "NODE_ENV=test webpack --mode=development && TS_NODE_PROJECT='./test/tsconfig.json' nyc --nycrc-path=test/.nycrc-mocha.json mocha --require ts-node/register --colors --config=test/.mocharc.yml",
"test:mocha": "NODE_ENV=test webpack --mode=development && NODE_ENV=test TS_NODE_PROJECT='./test/tsconfig.json' nyc --nycrc-path=test/.nycrc-mocha.json mocha --require ts-node/register --colors --config=test/.mocharc.yml",
"watch": "webpack --watch"
},
"keywords": [
@ -152,6 +152,7 @@
"ts-loader": "9.3.0",
"ts-migrate": "0.1.28",
"ts-node": "10.7.0",
"ts-sinon": "2.0.2",
"tsconfig-paths": "3.14.1",
"tsconfig-paths-webpack-plugin": "3.5.2",
"typescript": "4.7.2",

View file

@ -103,8 +103,8 @@ class Client {
mentions!: Mention[];
manager!: ClientManager;
messageStorage!: MessageStorage[];
highlightRegex?: RegExp;
highlightExceptionRegex?: RegExp;
highlightRegex!: RegExp | null;
highlightExceptionRegex!: RegExp | null;
messageProvider?: SqliteMessageStorage;
fileHash!: string;
@ -237,7 +237,7 @@ class Client {
connect(args: Record<string, any>, isStartup = false) {
const client = this;
const channels: Chan[] = [];
let channels: Chan[] = [];
// Get channel id for lobby before creating other channels for nicer ids
const lobbyChannelId = client.idChan++;
@ -270,6 +270,21 @@ class Client {
"' has an invalid channel which has been ignored"
);
}
// `join` is kept for backwards compatibility when updating from versions <2.0
// also used by the "connect" window
} else if (args.join) {
channels = args.join
.replace(/,/g, " ")
.split(/\s+/g)
.map((chan: string) => {
if (!chan.match(/^[#&!+]/)) {
chan = `#${chan}`;
}
return client.createChannel({
name: chan,
});
});
}
// TODO; better typing for args
@ -490,9 +505,9 @@ class Client {
}
compileCustomHighlights() {
function compileHighlightRegex(customHighlightString) {
function compileHighlightRegex(customHighlightString: string) {
if (typeof customHighlightString !== "string") {
return undefined;
return null;
}
// Ensure we don't have empty strings in the list of highlights
@ -502,7 +517,7 @@ class Client {
.filter((highlight) => highlight.length > 0);
if (highlightsTokens.length === 0) {
return undefined;
return null;
}
return new RegExp(

View file

@ -17,6 +17,7 @@ program
return;
}
// eslint-disable-next-line @typescript-eslint/no-var-requires
const ClientManager = require("../../clientManager");
const users = new ClientManager().getUsers();

View file

@ -42,7 +42,7 @@ class Utils {
}
// Parses CLI options such as `-c public=true`, `-c debug.raw=true`, etc.
static parseConfigOptions(this: void, val: string, memo: any) {
static parseConfigOptions(this: void, val: string, memo?: any) {
// Invalid option that is not of format `key=value`, do nothing
if (!val.includes("=")) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return

View file

@ -238,7 +238,6 @@ class Config {
}
const manifestPath = Utils.getFileFromRelativeToRoot("public", "thelounge.webmanifest");
console.log("manifest", manifestPath);
// Check if manifest exists, if not, the app most likely was not built
if (!fs.existsSync(manifestPath)) {

View file

@ -48,13 +48,16 @@ class Chan {
type!: ChanType;
state!: ChanState;
// TODO: this only exists when it's a query... should be better typed
// These are added to the channel elsewhere and should not be saved.
userAway!: boolean;
special?: SpecialChanType;
data?: any;
closed?: boolean;
num_users?: number;
// temporary for getFilteredClone until the above are moved out
keysToIgnore = ["userAway", "special", "data", "closed", "num_users"];
constructor(attr?: Partial<Chan>) {
_.defaults(this, attr, {
id: 0,
@ -194,9 +197,11 @@ class Chan {
* If true, channel is assumed active.
* @param {int} lastMessage - Last message id seen by active client to avoid sending duplicates.
*/
getFilteredClone(lastActiveChannel: number | boolean, lastMessage?: number): FilteredChannel {
getFilteredClone(lastActiveChannel?: number | boolean, lastMessage?: number): FilteredChannel {
return Object.keys(this).reduce((newChannel, prop) => {
if (prop === "users") {
if (this.keysToIgnore.includes(prop) || prop === "keysToIgnore") {
// no-op
} else if (prop === "users") {
// Do not send users, client requests updated user list whenever needed
newChannel[prop] = [];
} else if (prop === "messages") {

View file

@ -480,7 +480,7 @@ class Network {
}
}
getFilteredClone(lastActiveChannel: number, lastMessage: number) {
getFilteredClone(lastActiveChannel?: number, lastMessage?: number) {
const filteredNetwork = Object.keys(this).reduce((newNetwork, prop) => {
if (prop === "channels") {
// Channels objects perform their own cloning

View file

@ -60,6 +60,7 @@ async function fetch() {
// Add expiration date to the data to send to the client for later refresh
versions.expiresAt = time + TIME_TO_LIVE;
} catch (error) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
log.error(`Failed to fetch changelog: ${error}`);
}
@ -107,7 +108,7 @@ function updateVersions(response: Response<string>) {
}
function checkForUpdates(manager: ClientManager) {
fetch().then((versionData) => {
void fetch().then((versionData) => {
if (!module.exports.isUpdateAvailable) {
// Check for updates every 24 hours + random jitter of <3 hours
setTimeout(

View file

@ -10,7 +10,7 @@ const ctcpResponses = {
Object.getOwnPropertyNames(ctcpResponses)
.filter((key) => key !== "CLIENTINFO" && typeof ctcpResponses[key] === "function")
.join(" "),
PING: ({message}) => message.substring(5),
PING: ({message}: {message: string}) => message.substring(5),
SOURCE: () => pkg.repository.url,
VERSION: () => pkg.name + " " + Helper.getVersion() + " -- " + pkg.homepage,
};
@ -78,6 +78,7 @@ export default <IrcEventHandler>function (irc, network) {
type: MessageType.CTCP_REQUEST,
time: data.time,
from: new User({nick: target}),
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
hostmask: data.ident + "@" + data.hostname,
ctcpMessage: data.message,
});

View file

@ -58,6 +58,8 @@ export default <IrcEventHandler>function (irc, network) {
if (irc.connection.registered === false) {
const nickLen = parseInt(network.irc.network.options.NICKLEN, 10) || 16;
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
const random = (data.nick || irc.user.nick) + Math.floor(Math.random() * 10);
// Safeguard nick changes up to allowed length

View file

@ -27,12 +27,14 @@ export type LinkPreview = {
thumb: string;
size: number;
link: string; // Send original matched link to the client
shown?: boolean;
shown?: boolean | null;
error?: string;
message?: string;
media: string;
mediaType: string;
media?: string;
mediaType?: string;
maxSize?: number;
thumbActualUrl?: string;
};
export default function (client: Client, chan: Chan, msg: Msg, cleanText: string) {
@ -65,11 +67,7 @@ export default function (client: Client, chan: Chan, msg: Msg, cleanText: string
thumb: "",
size: -1,
link: link.link, // Send original matched link to the client
shown: undefined,
error: undefined,
message: undefined,
media: "",
mediaType: "",
shown: null,
};
cleanLinks.push(preview);
@ -244,7 +242,7 @@ function parseHtmlMedia($: any, preview, client: Client) {
}
// TODO: type preview
function parse(msg: Msg, chan: Chan, preview: any, res, client: Client) {
function parse(msg: Msg, chan: Chan, preview: LinkPreview, res, client: Client) {
let promise;
preview.size = res.size;
@ -338,7 +336,7 @@ function parse(msg: Msg, chan: Chan, preview: any, res, client: Client) {
promise.then((newRes) => handlePreview(client, chan, msg, preview, newRes));
}
function handlePreview(client, chan, msg, preview, res) {
function handlePreview(client: Client, chan: Chan, msg: Msg, preview: LinkPreview, res) {
const thumb = preview.thumbActualUrl || "";
delete preview.thumbActualUrl;
@ -386,7 +384,7 @@ function emitPreview(client: Client, chan: Chan, msg: Msg, preview: LinkPreview)
});
}
function removePreview(msg, preview) {
function removePreview(msg: Msg, preview: LinkPreview) {
// If a preview fails to load, remove the link from msg object
// So that client doesn't attempt to display an preview on page reload
const index = msg.previews.indexOf(preview);

View file

@ -10,6 +10,7 @@ export default <IrcEventHandler>function (irc, network) {
const msg = new Msg({
type: MessageType.LOGIN,
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
text: "Logged in as: " + data.account,
});
lobby.pushMessage(client, msg, true);

View file

@ -43,6 +43,7 @@ export default <IrcEventHandler>function (irc, network) {
if (data.error) {
msg = new Msg({
type: MessageType.ERROR,
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
text: "No such nick: " + data.nick,
});
} else {

View file

@ -213,15 +213,10 @@ class SqliteMessageStorage implements ISqliteMessageStorage {
}) as Promise<Message[]>;
}
search(query: SearchQuery): Promise<SearchResponse> {
search(query: SearchQuery): Promise<SearchResponse | []> {
if (!this.isEnabled) {
return Promise.resolve({
results: [],
target: "",
networkUuid: "",
offset: 0,
searchTerm: query?.searchTerm,
});
// TODO: this should return an empty SearchResponse?
return Promise.resolve([]);
}
// Using the '@' character to escape '%' and '_' in patterns.

View file

@ -29,15 +29,17 @@ export type SearchQuery = {
offset: string;
};
export type SearchResponse = Omit<SearchQuery, "channelName" | "offset"> & {
results: Message[];
target: string;
offset: number;
};
export type SearchResponse =
| (Omit<SearchQuery, "channelName" | "offset"> & {
results: Message[];
target: string;
offset: number;
})
| [];
type SearchFunction = (query: SearchQuery) => Promise<SearchResponse>;
export interface SqliteMessageStorage extends MessageStorage {
database: Database;
search: SearchFunction;
search: SearchFunction | [];
}

View file

@ -22,6 +22,7 @@ class STSPolicies {
constructor() {
this.stsFile = path.join(Config.getHomePath(), "sts-policies.json");
this.policies = new Map();
// eslint-disable-next-line @typescript-eslint/unbound-method
this.refresh = _.debounce(this.saveFile, 10000, {maxWait: 60000});
if (!fs.existsSync(this.stsFile)) {

View file

@ -67,17 +67,17 @@ class Uploader {
});
}
static createTokenTimeout(token: string) {
static createTokenTimeout(this: void, token: string) {
return setTimeout(() => uploadTokens.delete(token), 60 * 1000);
}
// TODO: type
static router(express: any) {
static router(this: void, express: any) {
express.get("/uploads/:name/:slug*?", Uploader.routeGetFile);
express.post("/uploads/new/:token", Uploader.routeUploadFile);
}
static async routeGetFile(req: Request, res: Response) {
static async routeGetFile(this: void, req: Request, res: Response) {
const name = req.params.name;
const nameRegex = /^[0-9a-f]{16}$/;
@ -131,7 +131,7 @@ class Uploader {
return res.sendFile(filePath);
}
static routeUploadFile(req: Request, res: Response) {
static routeUploadFile(this: void, req: Request, res: Response) {
let busboyInstance: NodeJS.WritableStream | busboy | null | undefined;
let uploadUrl: string | URL;
let randomName: string;
@ -223,7 +223,9 @@ class Uploader {
try {
fs.mkdirSync(destDir, {recursive: true});
} catch (err: any) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
log.error(`Error ensuring ${destDir} exists for uploads: ${err.message}`);
return abortWithError(err);
}
@ -325,6 +327,7 @@ class Uploader {
return "application/octet-stream";
} catch (e: any) {
if (e.code !== "ENOENT") {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
log.warn(`Failed to read ${filePath}: ${e.message}`);
}
}

View file

@ -84,7 +84,9 @@ class WebPush {
WebPushAPI.sendNotification(subscription, JSON.stringify(payload)).catch((error) => {
if (error.statusCode >= 400 && error.statusCode < 500) {
log.warn(
`WebPush subscription for ${client.name} returned an error (${error.statusCode}), removing subscription`
`WebPush subscription for ${client.name} returned an error (${
error.statusCode as string
}), removing subscription`
);
_.forOwn(client.config.sessions, ({pushSubscription}, token) => {
@ -96,7 +98,7 @@ class WebPush {
return;
}
log.error(`WebPush Error (${error})`);
log.error(`WebPush Error (${error as string})`);
});
}
}

View file

@ -1,3 +1,4 @@
// @ts-nocheck
// https://raw.githubusercontent.com/eternagame/HTML-Chat/vue-rewrite/src/app/types/modules/irc-framework/irc-framework.d.ts
// TODO: Fix this
declare module "irc-framework" {
@ -320,6 +321,12 @@ declare module "irc-framework" {
host: string;
}
export interface ChannelInfoEventArgs {
channel: string;
created_at?: number;
modes?: Mode[]; // TODO: check type
url?: string;
}
class IrcChannel extends EventEmitter {
constructor(irc_client: Client, channel_name: string, key: string);
@ -354,7 +361,7 @@ declare module "irc-framework" {
* one_way (false) Only relay messages to target_chan, not the reverse
* replay_nicks (true) Include the sending nick as part of the relayed message
*/
relay(target_chan: IrcChannel | string, opts: Object): void;
relay(target_chan: IrcChannel | string, opts: Record<string, any>): void;
// stream(stream_ops: Object): DuplexStream;
@ -364,12 +371,7 @@ declare module "irc-framework" {
on(eventType: string | symbol, cb: (event: any) => any): this;
}
export interface ChannelInfoEventArgs {
channel: string;
created_at?: number;
modes?: Mode[]; // TODO: check type
url?: string;
}
export interface UserListEventArgs {
channel: string;
users: IrcUser[]; // TODO: check type

View file

@ -116,7 +116,7 @@ interface ClientToServerEvents {
target: number;
messageIds?: number[];
msgId?: number;
shown?: boolean;
shown?: boolean | null;
}) => void;
"network:get": (uuid: string) => void;

View file

@ -3,7 +3,7 @@
"all": true,
"instrument": true,
"temp-dir": "./node_modules/.cache/nyc_output",
"exclude": ["webpack.config*.js"],
"exclude": ["webpack.config*.ts"],
"include": ["defaults", "src", "*.ts"],
"reporter": ["json", "text-summary"],
"clean": false

View file

@ -1,5 +1,5 @@
import {expect} from "chai";
import {stub} from "sinon";
import sinon from "ts-sinon";
import Auth from "../../../client/js/auth";
import localStorage from "../../../client/js/localStorage";
import location from "../../../client/js/location";
@ -7,15 +7,12 @@ import location from "../../../client/js/location";
describe("Auth", function () {
describe(".signout", function () {
beforeEach(function () {
stub(localStorage, "clear");
stub(location, "reload");
sinon.stub(localStorage, "clear");
sinon.stub(location, "reload");
});
afterEach(function () {
// @ts-expect- ts-migrate(2339) FIXME: Property 'restore' does not exist on type '() => v... Remove this comment to see the full error message
localStorage.clear.restore();
// @ts-expect-error ts-migrate(2339) FIXME: Property 'restore' does not exist on type '{ (): v... Remove this comment to see the full error message
location.reload.restore();
sinon.restore();
});
it("should empty the local storage", function () {

View file

@ -1,8 +1,10 @@
// @ts-nocheck TODO re-enable
"use strict";
import {expect} from "chai";
import Client from "../../src/client";
import Chan from "../../src/models/chan";
import Chan, {ChanType} from "../../src/models/chan";
import ModeCommand from "../../src/plugins/inputs/mode";
describe("Commands", function () {
@ -33,7 +35,17 @@ describe("Commands", function () {
testableNetwork.lastCommand = args.join(" ");
},
},
} as any;
} as {
firstCommand: string | null;
lastCommand: string | null;
nick: string;
irc: {
network: {
supports(type: string): string;
};
raw(...args: string[]): void;
};
};
const testableNetworkNoSupports = Object.assign({}, testableNetwork, {
irc: {
@ -49,7 +61,7 @@ describe("Commands", function () {
},
});
it("should not mess with the given target", function () {
it("should not mess with the given target", function (this: CommandContext) {
const test = function (expected: string, args: string[]) {
ModeCommand.input(testableNetwork, channel, "mode", Array.from(args));
expect(testableNetwork.lastCommand).to.equal(expected);

View file

@ -0,0 +1,8 @@
[
{
"host": "irc.example.com",
"port": 7000,
"duration": 3600,
"expires": 1654029411770
}
]

View file

@ -3,6 +3,7 @@ import {expect} from "chai";
import Chan from "../../src/models/chan";
import Msg from "../../src/models/msg";
import Network from "../../src/models/network";
import Prefix from "../../src/models/prefix";
import User from "../../src/models/user";
describe("Chan", function () {
@ -84,7 +85,7 @@ describe("Chan", function () {
describe("#getSortedUsers(irc)", function () {
const getUserNames = function (chan: Chan) {
return chan.getSortedUsers(network).map((u) => u.nick);
return chan.getSortedUsers(network as Network["irc"]).map((u) => u.nick);
};
it("returns unsorted list on null irc object", function () {

View file

@ -4,6 +4,7 @@ import {expect} from "chai";
import Msg from "../../src/models/msg";
import User from "../../src/models/user";
import {LinkPreview} from "../../src/plugins/irc-events/link";
describe("Msg", function () {
["from", "target"].forEach((prop) => {
@ -14,12 +15,12 @@ describe("Msg", function () {
modes: ["o"],
nick: "foo",
},
prefixLookup
prefixLookup as any
);
const msg = new Msg({[prop]: user});
// Mutating the user
user.setModes(["a"], prefixLookup);
user.setModes(["a"], prefixLookup as any);
user.nick = "bar";
// Message's `.from`/etc. should still refer to the original user
@ -46,11 +47,11 @@ describe("Msg", function () {
type: "link",
shown: true,
},
],
] as LinkPreview[],
});
it("should find a preview given an existing link", function () {
expect(msg.findPreview("https://thelounge.chat/").head).to.equal("The Lounge");
expect(msg.findPreview("https://thelounge.chat/")?.head).to.equal("The Lounge");
});
it("should not find a preview that does not exist", function () {

View file

@ -1,10 +1,11 @@
/* eslint-disable @typescript-eslint/no-empty-function */
"use strict";
import {expect} from "chai";
import Chan, {ChanType} from "../../src/models/chan";
import Msg from "../../src/models/msg";
import User from "../../src/models/user";
import Network, {NetworkWithIrcFramework} from "../../src/models/network";
import Network from "../../src/models/network";
import Config from "../../src/config";
import STSPolicies from "../../src/plugins/sts";
import ClientCertificate from "../../src/plugins/clientCertificate";
@ -177,6 +178,7 @@ describe("Network", function () {
});
it("should apply STS policies iff they match", function () {
// eslint-disable-next-line @typescript-eslint/no-empty-function
const client = {idMsg: 1, emit() {}} as any;
STSPolicies.update("irc.example.com", 7000, 3600);
@ -212,11 +214,11 @@ describe("Network", function () {
(network as any).createIrcFramework(client);
expect(network.irc).to.not.be.null;
const client_cert = network.irc.options.client_certificate;
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)).to.be.true;
expect(network.validate(client as any)).to.be.true;
expect(ClientCertificate.get(network.uuid)).to.deep.equal(client_cert); // Should be unchanged
@ -234,7 +236,7 @@ describe("Network", function () {
(network as any).createIrcFramework(client);
expect(network.irc).to.not.be.null;
const client_cert = network.irc.options.client_certificate;
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);
@ -311,7 +313,11 @@ describe("Network", function () {
expect(saveCalled).to.be.true;
expect(nameEmitCalled).to.be.true;
expect(network.uuid).to.not.equal("newuuid");
// @ts-ignore
expect(network.ip).to.be.undefined;
// @ts-ignore
expect(network.hostname).to.be.undefined;
expect(network.name).to.equal("Lounge Test Network");

View file

@ -5,8 +5,9 @@ import ldapAuth from "../../../src/plugins/auth/ldap";
import Config from "../../../src/config";
import ldap from "ldapjs";
import {expect} from "chai";
import {stub} from "sinon";
import TestUtil from "../../util";
import ClientManager from "../../../src/clientManager";
import sinon from "ts-sinon";
const user = "johndoe";
const wrongUser = "eve";
@ -16,8 +17,8 @@ const baseDN = "ou=accounts,dc=example,dc=com";
const primaryKey = "uid";
const serverPort = 1389;
function normalizeDN(dn) {
return ldap.parseDN(dn).toString();
function normalizeDN(dn: string) {
return ldap.parseDN(dn).toString() as string;
}
function startLdapServer(callback) {
@ -33,7 +34,7 @@ function startLdapServer(callback) {
authorizedUsers[normalizeDN(searchConf.rootDN)] = searchConf.rootPassword;
authorizedUsers[normalizeDN(userDN)] = correctPassword;
function authorize(req, res, next) {
function authorize(req: any, res: any, next: (error?: any) => void) {
const bindDN = req.connection.ldap.bindDN;
if (bindDN in authorizedUsers) {
@ -44,7 +45,7 @@ function startLdapServer(callback) {
}
Object.keys(authorizedUsers).forEach(function (dn) {
server.bind(dn, function (req, res, next) {
server.bind(dn, function (req, res, next: (error?: any) => void) {
const bindDN = req.dn.toString();
const password = req.credentials;
@ -86,11 +87,12 @@ function startLdapServer(callback) {
function testLdapAuth() {
// Create mock manager and client. When client is true, manager should not
// be used. But ideally the auth plugin should not use any of those.
const manager = {};
const manager = {} as ClientManager;
const client = true;
it("should successfully authenticate with correct password", function (done) {
ldapAuth.auth(manager, client, user, correctPassword, function (valid) {
// TODO: why is client = true?
ldapAuth.auth(manager, client as any, user, correctPassword, function (valid) {
expect(valid).to.equal(true);
done();
});
@ -98,26 +100,31 @@ function testLdapAuth() {
it("should fail to authenticate with incorrect password", function (done) {
let error = "";
stub(log, "error").callsFake(TestUtil.sanitizeLog((str) => (error += str)));
ldapAuth.auth(manager, client, user, wrongPassword, function (valid) {
const errorLogStub = sinon
.stub(log, "error")
.callsFake(TestUtil.sanitizeLog((str) => (error += str)));
ldapAuth.auth(manager, client as any, user, wrongPassword, function (valid) {
expect(valid).to.equal(false);
expect(error).to.equal(
"LDAP bind failed: InsufficientAccessRightsError: InsufficientAccessRightsError\n"
);
log.error.restore();
errorLogStub.restore();
done();
});
});
it("should fail to authenticate with incorrect username", function (done) {
let warning = "";
stub(log, "warn").callsFake(TestUtil.sanitizeLog((str) => (warning += str)));
const warnLogStub = sinon
.stub(log, "warn")
.callsFake(TestUtil.sanitizeLog((str) => (warning += str)));
ldapAuth.auth(manager, client, wrongUser, correctPassword, function (valid) {
ldapAuth.auth(manager, client as any, wrongUser, correctPassword, function (valid) {
expect(valid).to.equal(false);
expect(warning).to.equal("LDAP Search did not find anything for: eve (0)\n");
log.warn.restore();
warnLogStub.restore();
done();
});
});
@ -128,18 +135,21 @@ describe("LDAP authentication plugin", function () {
this.timeout(TestUtil.isRunningOnCI() ? 25000 : 5000);
this.slow(300);
let server;
let server: ldap.Server;
let infoLogStub: any;
before(function (done) {
stub(log, "info");
infoLogStub = sinon.stub(log, "info");
server = startLdapServer(done);
});
after(function () {
server.close();
server.close(() => {
// no-op
});
log.info.restore();
infoLogStub.restore();
});
beforeEach(function () {

View file

@ -3,7 +3,7 @@
import fs from "fs";
import path from "path";
import {expect} from "chai";
import ClientCertificate from "../../src/plugins/clientCertificate";
import ClientCertificate, {ClientCertificateType} from "../../src/plugins/clientCertificate";
import Config from "../../src/config";
describe("ClientCertificate", function () {
@ -16,12 +16,12 @@ describe("ClientCertificate", function () {
it("should generate a client certificate", function () {
Config.values.public = false;
const certificate = ClientCertificate.get("this-is-test-uuid");
const certificate = ClientCertificate.get("this-is-test-uuid") as ClientCertificateType;
expect(certificate.certificate).to.match(/^-----BEGIN CERTIFICATE-----/);
expect(certificate.private_key).to.match(/^-----BEGIN RSA PRIVATE KEY-----/);
const certificate2 = ClientCertificate.get("this-is-test-uuid");
const certificate2 = ClientCertificate.get("this-is-test-uuid") as ClientCertificateType;
expect(certificate2.certificate).to.equal(certificate.certificate);
expect(certificate2.private_key).to.equal(certificate.private_key);

View file

@ -1,10 +1,12 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/restrict-plus-operands */
"use strict";
import path from "path";
import {expect} from "chai";
import util from "../util";
import Config from "../../src/config";
import link from "../../src/plugins/irc-events/link.js";
import link, {LinkPreview} from "../../src/plugins/irc-events/link";
describe("Link plugin", function () {
// Increase timeout due to unpredictable I/O on CI services
@ -479,7 +481,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
res.send("<title>second title</title>");
});
const previews = [];
const previews: LinkPreview[] = [];
this.irc.on("msg:preview", function (data) {
if (data.preview.link === "http://localhost:" + port + "/one") {
@ -601,7 +603,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
res.send(`<title>${req.query.q}</title>`);
});
const previews = [];
const previews: LinkPreview[] = [];
this.irc.on("msg:preview", function (data) {
previews.push(data.preview.link);
@ -617,7 +619,9 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
}
if (previews.length === 5) {
expect(message.previews.map((preview) => preview.link)).to.have.members(previews);
expect(
message.previews.map((preview) => preview.link as LinkPreview)
).to.have.members(previews);
done();
}
});
@ -729,7 +733,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
text: "http://localhost:" + this.port + "/basic-og-once-lang",
});
const requests = [];
const requests: string[] = [];
let responses = 0;
this.irc.config.browser.language = "first language";

View file

@ -2,21 +2,25 @@
import log from "../../../src/log";
import {expect} from "chai";
import {stub} from "sinon";
import TestUtil from "../../util";
import sinon from "ts-sinon";
import packagePlugin from "../../../src/plugins/packages";
let packages;
let packages: typeof packagePlugin;
describe("packages", function () {
let logStub: sinon.SinonStub<string[], void>;
beforeEach(function () {
stub(log, "info");
logStub = sinon.stub(log, "info");
delete require.cache[require.resolve("../../../src/plugins/packages")];
packages = require("../../../src/plugins/packages");
// eslint-disable-next-line @typescript-eslint/no-var-requires
packages = require("../../../src/plugins/packages").default;
});
afterEach(function () {
log.info.restore();
logStub.restore();
});
describe(".getStylesheets", function () {
@ -46,10 +50,11 @@ describe("packages", function () {
describe(".loadPackages", function () {
it("should display report about loading packages", function () {
// Mock `log.info` to extract its effect into a string
log.info.restore();
logStub.restore();
let stdout = "";
stub(log, "info").callsFake(TestUtil.sanitizeLog((str) => (stdout += str)));
logStub = sinon
.stub(log, "info")
.callsFake(TestUtil.sanitizeLog((str) => (stdout += str)));
packages.loadPackages();
expect(stdout).to.deep.equal(

View file

@ -1,12 +1,14 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
"use strict";
import fs from "fs";
import path from "path";
import {expect} from "chai";
import util from "../util";
import Msg from "../../src/models/msg";
import Msg, {MessageType} from "../../src/models/msg";
import Config from "../../src/config";
import MessageStorage from "../../src/plugins/messageStorage/sqlite.js";
import MessageStorage from "../../src/plugins/messageStorage/sqlite";
import Client from "../../src/client";
describe("SQLite Message Storage", function () {
// Increase timeout due to unpredictable I/O on CI services
@ -14,13 +16,13 @@ describe("SQLite Message Storage", function () {
this.slow(300);
const expectedPath = path.join(Config.getHomePath(), "logs", "testUser.sqlite3");
let store;
let store: MessageStorage;
before(function (done) {
store = new MessageStorage({
name: "testUser",
idMsg: 1,
});
} as Client);
// Delete database file from previous test run
if (fs.existsSync(expectedPath)) {
@ -38,7 +40,7 @@ describe("SQLite Message Storage", function () {
});
it("should resolve an empty array when disabled", function () {
return store.getMessages(null, null).then((messages) => {
return store.getMessages(null as any, null as any).then((messages) => {
expect(messages).to.be.empty;
});
});
@ -94,14 +96,14 @@ describe("SQLite Message Storage", function () {
store.index(
{
uuid: "this-is-a-network-guid",
},
} as any,
{
name: "#thisISaCHANNEL",
},
} as any,
new Msg({
time: 123456789,
text: "Hello from sqlite world!",
})
} as any)
);
});
@ -110,10 +112,10 @@ describe("SQLite Message Storage", function () {
.getMessages(
{
uuid: "this-is-a-network-guid",
},
} as any,
{
name: "#thisisaCHANNEL",
}
} as any
)
.then((messages) => {
expect(messages).to.have.lengthOf(1);
@ -134,17 +136,20 @@ describe("SQLite Message Storage", function () {
for (let i = 0; i < 200; ++i) {
store.index(
{uuid: "retrieval-order-test-network"},
{name: "#channel"},
{uuid: "retrieval-order-test-network"} as any,
{name: "#channel"} as any,
new Msg({
time: 123456789 + i,
text: `msg ${i}`,
})
} as any)
);
}
return store
.getMessages({uuid: "retrieval-order-test-network"}, {name: "#channel"})
.getMessages(
{uuid: "retrieval-order-test-network"} as any,
{name: "#channel"} as any
)
.then((messages) => {
expect(messages).to.have.lengthOf(2);
expect(messages.map((i) => i.text)).to.deep.equal(["msg 198", "msg 199"]);
@ -164,16 +169,18 @@ describe("SQLite Message Storage", function () {
.search({
searchTerm: "msg",
networkUuid: "retrieval-order-test-network",
})
} as any)
.then((messages) => {
// @ts-ignore
expect(messages.results).to.have.lengthOf(100);
const expectedMessages = [];
const expectedMessages: string[] = [];
for (let i = 100; i < 200; ++i) {
expectedMessages.push(`msg ${i}`);
}
// @ts-ignore
expect(messages.results.map((i) => i.text)).to.deep.equal(expectedMessages);
});
} finally {
@ -187,8 +194,9 @@ describe("SQLite Message Storage", function () {
.search({
searchTerm: query,
networkUuid: "this-is-a-network-guid2",
})
} as any)
.then((messages) => {
// @ts-ignore
expect(messages.results.map((i) => i.text)).to.deep.equal(expected);
});
}
@ -199,35 +207,38 @@ describe("SQLite Message Storage", function () {
Config.values.maxHistory = 3;
store.index(
{uuid: "this-is-a-network-guid2"},
{name: "#channel"},
{uuid: "this-is-a-network-guid2"} as any,
{name: "#channel"} as any,
new Msg({
time: 123456790,
text: `foo % bar _ baz`,
})
} as any)
);
store.index(
{uuid: "this-is-a-network-guid2"},
{name: "#channel"},
{uuid: "this-is-a-network-guid2"} as any,
{name: "#channel"} as any,
new Msg({
time: 123456791,
text: `foo bar x baz`,
})
} as any)
);
store.index(
{uuid: "this-is-a-network-guid2"},
{name: "#channel"},
{uuid: "this-is-a-network-guid2"} as any,
{name: "#channel"} as any,
new Msg({
time: 123456792,
text: `bar @ baz`,
})
} as any)
);
return (
store
.getMessages({uuid: "this-is-a-network-guid2"}, {name: "#channel"})
.getMessages(
{uuid: "this-is-a-network-guid2"} as any,
{name: "#channel"} as any
)
// .getMessages() waits for store.index() transactions to commit
.then(() => assertResults("foo", ["foo % bar _ baz", "foo bar x baz"]))
.then(() => assertResults("%", ["foo % bar _ baz"]))

View file

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/restrict-plus-operands */
"use strict";
import fs from "fs";
@ -7,7 +8,7 @@ import {expect} from "chai";
import util from "../util";
import Config from "../../src/config";
import storage from "../../src/plugins/storage";
import link from "../../src/plugins/irc-events/link.js";
import link from "../../src/plugins/irc-events/link";
import {Request, Response} from "express";
describe("Image storage", function () {

View file

@ -3,33 +3,31 @@
import log from "../src/log";
import Config from "../src/config";
import {expect} from "chai";
import {stub} from "sinon";
import got from "got";
import io from "socket.io-client";
import util from "./util";
import changelog from "../src/plugins/changelog";
import Client from "../src/client";
import Server from "../src/Server";
import sinon from "ts-sinon";
describe("Server", function () {
// Increase timeout due to unpredictable I/O on CI services
this.timeout(util.isRunningOnCI() ? 25000 : 5000);
let server: any;
let server;
before(async function () {
stub(log, "info");
stub(changelog, "checkForUpdates");
server = Server();
sinon.stub(log, "info");
sinon.stub(changelog, "checkForUpdates");
server = await (await import("../src/server")).default({} as any);
});
after(function (done) {
server.close(done);
log.info.restore();
changelog.checkForUpdates.restore();
sinon.restore();
});
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
const webURL = `http://${Config.values.host}:${Config.values.port}/`;
describe("Express", () => {
@ -53,7 +51,7 @@ describe("Server", function () {
describe("WebSockets", function () {
this.slow(300);
let client: Client;
let client: ReturnType<typeof io>;
beforeEach(() => {
client = io(webURL, {

View file

@ -1,21 +1,21 @@
"use strict";
const log = require("../../../src/log");
import log from "../../../src/log";
import {expect} from "chai";
const stub = require("sinon").stub;
const TestUtil = require("../../util");
const Utils = require("../../../src/command-line/utils");
import TestUtil from "../../util";
import Utils from "../../../src/command-line/utils";
import sinon from "ts-sinon";
describe("Utils", function () {
describe(".extraHelp", function () {
afterEach(function () {
log.raw.restore();
sinon.restore();
});
it("should start and end with empty lines to display correctly with --help", function () {
// Mock `log.raw` to extract its effect into an array
const stdout = [];
stub(log, "raw").callsFake(TestUtil.sanitizeLog((str) => stdout.push(str)));
const stdout: string[] = [];
sinon.stub(log).raw.callsFake(TestUtil.sanitizeLog((str) => stdout.push(str)));
Utils.extraHelp();
@ -31,7 +31,7 @@ describe("Utils", function () {
it("should contain information about THELOUNGE_HOME env var", function () {
// Mock `log.raw` to extract its effect into a concatenated string
let stdout = "";
stub(log, "raw").callsFake(TestUtil.sanitizeLog((str) => (stdout += str)));
sinon.stub(log).raw.callsFake(TestUtil.sanitizeLog((str) => (stdout += str)));
Utils.extraHelp();
@ -129,11 +129,11 @@ describe("Utils", function () {
describe("when given the same key multiple times", function () {
afterEach(function () {
log.warn.restore();
sinon.restore();
});
it("should not override options", function () {
stub(log, "warn");
sinon.stub(log, "warn");
expect(Utils.parseConfigOptions("foo=baz", {foo: "bar"})).to.deep.equal({
foo: "bar",
@ -142,7 +142,9 @@ describe("Utils", function () {
it("should display a warning", function () {
let warning = "";
stub(log, "warn").callsFake(TestUtil.sanitizeLog((str) => (warning += str)));
sinon
.stub(log, "warn")
.callsFake(TestUtil.sanitizeLog((str) => (warning += str)));
Utils.parseConfigOptions("foo=bar", {foo: "baz"});

View file

@ -1,8 +1,8 @@
"use strict";
import {expect} from "chai";
const os = require("os");
const Helper = require("../../src/helper");
import os from "os";
import Helper from "../../src/helper";
describe("Helper", function () {
describe("#expandHome", function () {
@ -35,7 +35,7 @@ describe("Helper", function () {
});
it("should return an empty string when given undefined", function () {
expect(Helper.expandHome(undefined)).to.equal("");
expect(Helper.expandHome(undefined as any)).to.equal("");
});
});

View file

@ -1,14 +1,15 @@
"use strict";
import {expect} from "chai";
import {stub} from "sinon";
import log from "../../src/log";
import Client from "../../src/client";
import TestUtil from "../util";
import sinon from "ts-sinon";
describe("Custom highlights", function () {
let userLoadedLog = "";
stub(log, "info").callsFake(TestUtil.sanitizeLog((str) => (userLoadedLog += str)));
const logInfoStub = sinon.stub(log, "info");
logInfoStub.callsFake(TestUtil.sanitizeLog((str) => (userLoadedLog += str)));
const client = new Client(
{
@ -26,10 +27,9 @@ describe("Custom highlights", function () {
highlights: "foo, @all, sp ace , 고",
highlightExceptions: "foo bar, bar @all, test sp ace test",
},
}
} as any
);
log.info.restore();
logInfoStub.restore();
expect(userLoadedLog).to.equal("User test loaded\n");
it("should NOT highlight", function () {

View file

@ -1,7 +1,7 @@
"use strict";
import {expect} from "chai";
const Helper = require("../../src/helper");
import Helper from "../../src/helper";
describe("Hostmask", function () {
it(".parseHostmask", function () {

View file

@ -1,21 +1,22 @@
"use strict";
const log = require("../../src/log");
import {expect} from "chai";
const stub = require("sinon").stub;
const Config = require("../../src/config");
const TestUtil = require("../util");
import sinon from "ts-sinon";
import log from "../../src/log";
import Config from "../../src/config";
import TestUtil from "../util";
describe("mergeConfig", function () {
it("should mutate object", function () {
const config = {
ip: "default",
};
} as any;
expect(
Config._merge_config_objects(config, {
ip: "overridden",
})
} as any)
).to.deep.equal({
ip: "overridden",
});
@ -31,10 +32,10 @@ describe("mergeConfig", function () {
{
ip: "default",
newProp: "this should appear too",
},
} as any,
{
ip: "overridden",
}
} as any
)
).to.deep.equal({
ip: "overridden",
@ -47,13 +48,13 @@ describe("mergeConfig", function () {
Config._merge_config_objects(
{
tlsOptions: {},
},
} as any,
{
tlsOptions: {
user: "test",
thing: 123,
},
}
} as any
)
).to.deep.equal({
tlsOptions: {
@ -65,24 +66,24 @@ describe("mergeConfig", function () {
it("should warn for unknown top level keys", function () {
let warning = "";
stub(log, "warn").callsFake(TestUtil.sanitizeLog((str) => (warning += str)));
sinon.stub(log, "warn").callsFake(TestUtil.sanitizeLog((str) => (warning += str)));
expect(
Config._merge_config_objects(
{
optionOne: 123,
},
} as any,
{
optionOne: 456,
optionTwo: 789,
}
} as any
)
).to.deep.equal({
optionOne: 456,
optionTwo: 789,
});
log.warn.restore();
sinon.restore();
expect(warning).to.equal('Unknown key "optionTwo", please verify your config.\n');
});
@ -93,13 +94,13 @@ describe("mergeConfig", function () {
optionOne: {
subOne: 123,
},
},
} as any,
{
optionOne: {
subOne: 123,
subTwo: 123,
},
}
} as any
)
).to.deep.equal({
optionOne: {
@ -114,10 +115,10 @@ describe("mergeConfig", function () {
Config._merge_config_objects(
{
oidentd: null,
},
} as any,
{
oidentd: "some path",
}
} as any
)
).to.deep.equal({
oidentd: "some path",
@ -129,13 +130,13 @@ describe("mergeConfig", function () {
Config._merge_config_objects(
{
webirc: null,
},
} as any,
{
webirc: {
serverone: "password",
servertwo: "password2",
},
}
} as any
)
).to.deep.equal({
webirc: {
@ -152,12 +153,12 @@ describe("mergeConfig", function () {
Config._merge_config_objects(
{
webirc: null,
},
} as any,
{
webirc: {
servercb: callbackFunction,
},
}
} as any
)
).to.deep.equal({
webirc: {
@ -180,7 +181,7 @@ describe("mergeConfig", function () {
newThing: "but also this",
},
},
},
} as any,
{
nestedOnce: {},
nestedTwice: {
@ -188,7 +189,7 @@ describe("mergeConfig", function () {
otherThing: "overridden",
},
},
}
} as any
)
).to.deep.equal({
nestedOnce: {
@ -209,10 +210,10 @@ describe("mergeConfig", function () {
Config._merge_config_objects(
{
test: ["sqlite", "text"],
},
} as any,
{
test: ["sqlite"],
}
} as any
)
).to.deep.equal({
test: ["sqlite"],
@ -222,10 +223,10 @@ describe("mergeConfig", function () {
Config._merge_config_objects(
{
test: ["sqlite", "text"],
},
} as any,
{
test: [],
}
} as any
)
).to.deep.equal({
test: [],
@ -237,10 +238,10 @@ describe("mergeConfig", function () {
Config._merge_config_objects(
{
test: ["sqlite", "text"],
},
} as any,
{
test: ["text", "sqlite"],
}
} as any
)
).to.deep.equal({
test: ["text", "sqlite"],
@ -248,7 +249,7 @@ describe("mergeConfig", function () {
});
it("should only merge same type", function () {
stub(log, "warn");
sinon.stub(log, "warn");
expect(
Config._merge_config_objects(
@ -256,10 +257,10 @@ describe("mergeConfig", function () {
shouldBeObject: {
thing: "yes",
},
},
} as any,
{
shouldBeObject: "bad type",
}
} as any
)
).to.deep.equal({
shouldBeObject: {
@ -271,15 +272,15 @@ describe("mergeConfig", function () {
Config._merge_config_objects(
{
shouldBeString: "string",
},
} as any,
{
shouldBeString: 1234567,
}
} as any
)
).to.deep.equal({
shouldBeString: "string",
});
log.warn.restore();
sinon.restore();
});
});

View file

@ -2,7 +2,7 @@
import {expect} from "chai";
const Network = require("../../src/models/network");
import Network from "../../src/models/network";
const network = new Network({name: "networkName"});
@ -10,54 +10,54 @@ describe("Nickname highlights", function () {
it("should NOT highlight nickname", function () {
network.setNick("lounge-bot");
expect("").to.not.match(network.highlightRegex);
expect(" ").to.not.match(network.highlightRegex);
expect("completely unrelated sentence").to.not.match(network.highlightRegex);
expect("foobarlounge-bot").to.not.match(network.highlightRegex);
expect("lounge-botfoobar").to.not.match(network.highlightRegex);
expect("\x03123lounge-bot").to.not.match(network.highlightRegex);
expect("lo\x0312unge-bot").to.not.match(network.highlightRegex);
expect("123lounge-bot").to.not.match(network.highlightRegex);
expect("lounge-botz").to.not.match(network.highlightRegex);
expect("lounge-bot123").to.not.match(network.highlightRegex);
expect("lounge- bot").to.not.match(network.highlightRegex);
expect("lounge_bot").to.not.match(network.highlightRegex);
expect("lounge- bot").to.not.match(network.highlightRegex);
expect("Alounge-bot").to.not.match(network.highlightRegex);
expect("lounge-botW").to.not.match(network.highlightRegex);
expect("").to.not.match(network.highlightRegex as any);
expect(" ").to.not.match(network.highlightRegex as any);
expect("completely unrelated sentence").to.not.match(network.highlightRegex as any);
expect("foobarlounge-bot").to.not.match(network.highlightRegex as any);
expect("lounge-botfoobar").to.not.match(network.highlightRegex as any);
expect("\x03123lounge-bot").to.not.match(network.highlightRegex as any);
expect("lo\x0312unge-bot").to.not.match(network.highlightRegex as any);
expect("123lounge-bot").to.not.match(network.highlightRegex as any);
expect("lounge-botz").to.not.match(network.highlightRegex as any);
expect("lounge-bot123").to.not.match(network.highlightRegex as any);
expect("lounge- bot").to.not.match(network.highlightRegex as any);
expect("lounge_bot").to.not.match(network.highlightRegex as any);
expect("lounge- bot").to.not.match(network.highlightRegex as any);
expect("Alounge-bot").to.not.match(network.highlightRegex as any);
expect("lounge-botW").to.not.match(network.highlightRegex as any);
});
it("should highlight nickname", function () {
network.setNick("lounge-bot");
expect("lounge-bot").to.match(network.highlightRegex);
expect("LoUnge-Bot").to.match(network.highlightRegex);
expect("LoUnge-Bot:hello").to.match(network.highlightRegex);
expect("lounge-bot, hello").to.match(network.highlightRegex);
expect("lounge-bot: hello").to.match(network.highlightRegex);
expect("lounge-bot hello").to.match(network.highlightRegex);
expect("\x0312lounge-bot").to.match(network.highlightRegex);
expect("lounge-bot\x0312 test").to.match(network.highlightRegex);
expect("|lounge-bot").to.match(network.highlightRegex);
expect("www.lounge-bot.example.com").to.match(network.highlightRegex);
expect(" lounge-bot").to.match(network.highlightRegex);
expect("@lounge-bot").to.match(network.highlightRegex);
expect("+lounge-bot").to.match(network.highlightRegex);
expect("lounge-bot_, hey").to.match(network.highlightRegex);
expect("lounge-bot-, hey").to.match(network.highlightRegex);
expect("lounge-bot|sleep, hey").to.match(network.highlightRegex);
expect("LOUNGE-bot|sleep, hey").to.match(network.highlightRegex);
expect("lounge-bot").to.match(network.highlightRegex as any);
expect("LoUnge-Bot").to.match(network.highlightRegex as any);
expect("LoUnge-Bot:hello").to.match(network.highlightRegex as any);
expect("lounge-bot, hello").to.match(network.highlightRegex as any);
expect("lounge-bot: hello").to.match(network.highlightRegex as any);
expect("lounge-bot hello").to.match(network.highlightRegex as any);
expect("\x0312lounge-bot").to.match(network.highlightRegex as any);
expect("lounge-bot\x0312 test").to.match(network.highlightRegex as any);
expect("|lounge-bot").to.match(network.highlightRegex as any);
expect("www.lounge-bot.example.com").to.match(network.highlightRegex as any);
expect(" lounge-bot").to.match(network.highlightRegex as any);
expect("@lounge-bot").to.match(network.highlightRegex as any);
expect("+lounge-bot").to.match(network.highlightRegex as any);
expect("lounge-bot_, hey").to.match(network.highlightRegex as any);
expect("lounge-bot-, hey").to.match(network.highlightRegex as any);
expect("lounge-bot|sleep, hey").to.match(network.highlightRegex as any);
expect("LOUNGE-bot|sleep, hey").to.match(network.highlightRegex as any);
});
it("changing name should update regex", function () {
network.setNick("lounge-bot");
expect("lounge-bot, hello").to.match(network.highlightRegex);
expect("cool_person, hello").to.not.match(network.highlightRegex);
expect("lounge-bot, hello").to.match(network.highlightRegex as any);
expect("cool_person, hello").to.not.match(network.highlightRegex as any);
network.setNick("cool_person");
expect("lounge-bot, hello").to.not.match(network.highlightRegex);
expect("cool_person, hello").to.match(network.highlightRegex);
expect("lounge-bot, hello").to.not.match(network.highlightRegex as any);
expect("cool_person, hello").to.match(network.highlightRegex as any);
});
});

View file

@ -1,7 +1,7 @@
"use strict";
import {expect} from "chai";
const Helper = require("../../src/helper");
import Helper from "../../src/helper";
describe("Client passwords", function () {
this.slow(1500);

View file

@ -1,7 +1,8 @@
"use strict";
import {expect} from "chai";
const TextFileMessageStorage = require("../../src/plugins/messageStorage/text");
import Network from "../../src/models/network";
import TextFileMessageStorage from "../../src/plugins/messageStorage/text";
describe("TextFileMessageStorage", function () {
it("should combine network name and uuid into a safe name", function () {
@ -9,7 +10,7 @@ describe("TextFileMessageStorage", function () {
TextFileMessageStorage.getNetworkFolderName({
name: "Freenode",
uuid: "f9042ec9-4016-45e0-a8a8-d378fb252628",
})
} as Network)
).to.equal("freenode-4016-45e0-a8a8-d378fb252628");
});
@ -18,7 +19,7 @@ describe("TextFileMessageStorage", function () {
TextFileMessageStorage.getNetworkFolderName({
name: '@ TeSt ../..\\<>:"/\\|?*',
uuid: "f9042ec9-4016-45e0-a8a8-d378fb252628",
})
} as Network)
).to.equal("@-test-.._..--45e0-a8a8-d378fb252628");
});
@ -27,7 +28,7 @@ describe("TextFileMessageStorage", function () {
TextFileMessageStorage.getNetworkFolderName({
name: "Freenod",
uuid: "f9042ec9-4016-45e0-a8a8-d378fb252628",
})
} as Network)
).to.equal("freenod--4016-45e0-a8a8-d378fb252628");
});
@ -36,7 +37,7 @@ describe("TextFileMessageStorage", function () {
TextFileMessageStorage.getNetworkFolderName({
name: "This network name is longer than the uuid itself but it should be limited",
uuid: "f9042ec9-4016-45e0-a8a8-d378fb252628",
})
} as Network)
).to.equal("this-network-name-is-lo-d378fb252628");
});
});

View file

@ -4,7 +4,6 @@ import * as webpack from "webpack";
import * as path from "path";
import CopyPlugin from "copy-webpack-plugin";
import MiniCssExtractPlugin from "mini-css-extract-plugin";
// TODO; we should add a declaration file
import {VueLoaderPlugin} from "vue-loader";
import babelConfig from "./babel.config.cjs";
import Helper from "./src/helper";
@ -121,7 +120,7 @@ const config: webpack.Configuration = {
{
from: path.resolve(
__dirname,
"node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff"
"node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff*"
),
to: "fonts/[name][ext]",
},

View file

@ -1349,7 +1349,7 @@
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f"
integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==
"@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.3":
"@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.1", "@sinonjs/commons@^1.8.3":
version "1.8.3"
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d"
integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==
@ -1363,6 +1363,22 @@
dependencies:
"@sinonjs/commons" "^1.7.0"
"@sinonjs/fake-timers@^6.0.0", "@sinonjs/fake-timers@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40"
integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==
dependencies:
"@sinonjs/commons" "^1.7.0"
"@sinonjs/samsam@^5.3.1":
version "5.3.1"
resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.3.1.tgz#375a45fe6ed4e92fca2fb920e007c48232a6507f"
integrity sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg==
dependencies:
"@sinonjs/commons" "^1.6.0"
lodash.get "^4.4.2"
type-detect "^4.0.8"
"@sinonjs/samsam@^6.1.1":
version "6.1.1"
resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-6.1.1.tgz#627f7f4cbdb56e6419fa2c1a3e4751ce4f6a00b1"
@ -1502,7 +1518,7 @@
"@types/node" "*"
"@types/responselike" "*"
"@types/chai@4.3.1":
"@types/chai@*", "@types/chai@4.3.1":
version "4.3.1"
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.1.tgz#e2c6e73e0bdeb2521d00756d099218e9f5d90a04"
integrity sha512-/zPMqDkzSZ8t3VtxOa4KPq7uzzW978M9Tvh+j7GHKuo6k6GTLxPJ4J5gE5cjfJ26pnXst0N5Hax8Sr0T2Mi9zQ==
@ -1677,7 +1693,7 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.31.tgz#a5bb84ecfa27eec5e1c802c6bbf8139bdb163a5d"
integrity sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==
"@types/node@^14.6.2":
"@types/node@^14.6.1", "@types/node@^14.6.2":
version "14.18.18"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.18.tgz#5c9503030df484ccffcbb935ea9a9e1d6fad1a20"
integrity sha512-B9EoJFjhqcQ9OmQrNorItO+OwEOORNn3S31WuiHvZY/dm9ajkB7AKD/8toessEtHHNL+58jofbq7hMMY9v4yig==
@ -1727,13 +1743,28 @@
"@types/mime" "^1"
"@types/node" "*"
"@types/sinon@10.0.11":
"@types/sinon-chai@^3.2.4":
version "3.2.8"
resolved "https://registry.yarnpkg.com/@types/sinon-chai/-/sinon-chai-3.2.8.tgz#5871d09ab50d671d8e6dd72e9073f8e738ac61dc"
integrity sha512-d4ImIQbT/rKMG8+AXpmcan5T2/PNeSjrYhvkwet6z0p8kzYtfgA32xzOBlbU0yqJfq+/0Ml805iFoODO0LP5/g==
dependencies:
"@types/chai" "*"
"@types/sinon" "*"
"@types/sinon@*", "@types/sinon@10.0.11":
version "10.0.11"
resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-10.0.11.tgz#8245827b05d3fc57a6601bd35aee1f7ad330fc42"
integrity sha512-dmZsHlBsKUtBpHriNjlK0ndlvEh8dcb9uV9Afsbt89QIyydpC7NcR+nWlAhASfy3GHnxTl4FX/aKE7XZUt/B4g==
dependencies:
"@types/sinonjs__fake-timers" "*"
"@types/sinon@^9.0.5":
version "9.0.11"
resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-9.0.11.tgz#7af202dda5253a847b511c929d8b6dda170562eb"
integrity sha512-PwP4UY33SeeVKodNE37ZlOsR9cReypbMJOhZ7BVE0lB+Hix3efCOxiJWiE5Ia+yL9Cn2Ch72EjFTRze8RZsNtg==
dependencies:
"@types/sinonjs__fake-timers" "*"
"@types/sinonjs__fake-timers@*":
version "8.1.2"
resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz#bf2e02a3dbd4aecaf95942ecd99b7402e03fad5e"
@ -3695,7 +3726,7 @@ diff@5.0.0, diff@^5.0.0:
resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==
diff@^4.0.1:
diff@^4.0.1, diff@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
@ -6603,6 +6634,17 @@ nice-try@^1.0.4:
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
nise@^4.0.4:
version "4.1.0"
resolved "https://registry.yarnpkg.com/nise/-/nise-4.1.0.tgz#8fb75a26e90b99202fa1e63f448f58efbcdedaf6"
integrity sha512-eQMEmGN/8arp0xsvGoQ+B1qvSkR73B1nWSCh7nOt5neMCtwcQVYQGdzQMhcNscktTsWB54xnlSQFzOAPJD8nXA==
dependencies:
"@sinonjs/commons" "^1.7.0"
"@sinonjs/fake-timers" "^6.0.0"
"@sinonjs/text-encoding" "^0.7.1"
just-extend "^4.0.2"
path-to-regexp "^1.7.0"
nise@^5.1.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.1.tgz#ac4237e0d785ecfcb83e20f389185975da5c31f3"
@ -8505,6 +8547,18 @@ sinon@13.0.0:
nise "^5.1.0"
supports-color "^7.2.0"
sinon@^9.0.3:
version "9.2.4"
resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.2.4.tgz#e55af4d3b174a4443a8762fa8421c2976683752b"
integrity sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg==
dependencies:
"@sinonjs/commons" "^1.8.1"
"@sinonjs/fake-timers" "^6.0.1"
"@sinonjs/samsam" "^5.3.1"
diff "^4.0.2"
nise "^4.0.4"
supports-color "^7.1.0"
slash@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
@ -9315,6 +9369,16 @@ ts-node@10.7.0:
v8-compile-cache-lib "^3.0.0"
yn "3.1.1"
ts-sinon@2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/ts-sinon/-/ts-sinon-2.0.2.tgz#9e020ad1e76b41e71cb7344b71923dbc3c6bc7ca"
integrity sha512-Eh6rXPQruACHPn+/e5HsIMaHZa17tGP/scGjUeW5eJ/Levn8hBV6zSP/6QkEDUP7wLkTyY0yeYikjpTzgC9Gew==
dependencies:
"@types/node" "^14.6.1"
"@types/sinon" "^9.0.5"
"@types/sinon-chai" "^3.2.4"
sinon "^9.0.3"
tsconfig-paths-webpack-plugin@3.5.2:
version "3.5.2"
resolved "https://registry.yarnpkg.com/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-3.5.2.tgz#01aafff59130c04a8c4ebc96a3045c43c376449a"