mirror of
https://github.com/thelounge/thelounge.git
synced 2024-05-19 22:56:37 +02:00
fix joining channels from net form(?), major progress in tests
This commit is contained in:
parent
16c6bcf0fc
commit
cff9209a25
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -182,7 +182,6 @@ export default defineComponent({
|
|||
return;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
return friendlysize(props.link.maxSize);
|
||||
});
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -17,6 +17,7 @@ program
|
|||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const ClientManager = require("../../clientManager");
|
||||
const users = new ClientManager().getUsers();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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") {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
14
src/plugins/messageStorage/types.d.ts
vendored
14
src/plugins/messageStorage/types.d.ts
vendored
|
@ -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 | [];
|
||||
}
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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}`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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})`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
16
src/types/modules/irc-framework.d.ts
vendored
16
src/types/modules/irc-framework.d.ts
vendored
|
@ -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
|
||||
|
|
2
src/types/socket-events.d.ts
vendored
2
src/types/socket-events.d.ts
vendored
|
@ -116,7 +116,7 @@ interface ClientToServerEvents {
|
|||
target: number;
|
||||
messageIds?: number[];
|
||||
msgId?: number;
|
||||
shown?: boolean;
|
||||
shown?: boolean | null;
|
||||
}) => void;
|
||||
|
||||
"network:get": (uuid: string) => void;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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);
|
||||
|
|
8
test/fixtures/.thelounge/sts-policies.json
vendored
Normal file
8
test/fixtures/.thelounge/sts-policies.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
[
|
||||
{
|
||||
"host": "irc.example.com",
|
||||
"port": 7000,
|
||||
"duration": 3600,
|
||||
"expires": 1654029411770
|
||||
}
|
||||
]
|
|
@ -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 () {
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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"]))
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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, {
|
||||
|
|
|
@ -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"});
|
||||
|
|
@ -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("");
|
||||
});
|
||||
});
|
||||
|
|
@ -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 () {
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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]",
|
||||
},
|
||||
|
|
74
yarn.lock
74
yarn.lock
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue