Compare commits

..

No commits in common. "411e3d38e097f5d2e293a3ee4ec4bf01f21fb321" and "747107169b162898a244a10a1464c45716bca09c" have entirely different histories.

4 changed files with 153 additions and 220 deletions

View File

@ -9,10 +9,9 @@
<h2 class="help-version-title"> <h2 class="help-version-title">
<span>About Hard Lounge</span> <span>About Hard Lounge</span>
<small> <small>
v{{ v{{ store.state.serverConfiguration?.version }} (<router-link
store.state.serverConfiguration?.version id="view-changelog"
}} to="/changelog"
(<router-link id="view-changelog" to="/changelog"
>release notes</router-link >release notes</router-link
>) >)
</small> </small>
@ -54,8 +53,8 @@
<div class="help-item"> <div class="help-item">
<div class="description"> <div class="description">
<p> <p>
IRC.SUPERNETS.ORG #SUPERBOWL FUCK YOUR NETWORK COLD HARD IRC.SUPERNETS.ORG #SUPERBOWL FUCK YOUR NETWORK COLD HARD CHATS THIS IS NOT
CHATS THIS IS NOT YOUR DADS FOOTBALL CHANNEL YOUR DADS FOOTBALL CHANNEL
</p> </p>
</div> </div>
</div> </div>
@ -94,9 +93,7 @@
<div class="help-item"> <div class="help-item">
<div class="subject"> <div class="subject">
<span v-if="!isApple" <span v-if="!isApple"><kbd>Alt</kbd> <kbd>Shift</kbd> <kbd></kbd></span>
><kbd>Alt</kbd> <kbd>Shift</kbd> <kbd></kbd></span
>
<span v-else><kbd></kbd> <kbd></kbd> <kbd></kbd></span> <span v-else><kbd></kbd> <kbd></kbd> <kbd></kbd></span>
</div> </div>
<div class="description"> <div class="description">
@ -106,9 +103,7 @@
<div class="help-item"> <div class="help-item">
<div class="subject"> <div class="subject">
<span v-if="!isApple" <span v-if="!isApple"><kbd>Alt</kbd> <kbd>Shift</kbd> <kbd></kbd></span>
><kbd>Alt</kbd> <kbd>Shift</kbd> <kbd></kbd></span
>
<span v-else><kbd></kbd> <kbd></kbd> <kbd></kbd></span> <span v-else><kbd></kbd> <kbd></kbd> <kbd></kbd></span>
</div> </div>
<div class="description"> <div class="description">
@ -118,9 +113,7 @@
<div class="help-item"> <div class="help-item">
<div class="subject"> <div class="subject">
<span v-if="!isApple" <span v-if="!isApple"><kbd>Alt</kbd> <kbd>Shift</kbd> <kbd></kbd></span>
><kbd>Alt</kbd> <kbd>Shift</kbd> <kbd></kbd></span
>
<span v-else><kbd></kbd> <kbd></kbd> <kbd></kbd></span> <span v-else><kbd></kbd> <kbd></kbd> <kbd></kbd></span>
</div> </div>
<div class="description"> <div class="description">
@ -130,9 +123,7 @@
<div class="help-item"> <div class="help-item">
<div class="subject"> <div class="subject">
<span v-if="!isApple" <span v-if="!isApple"><kbd>Alt</kbd> <kbd>Shift</kbd> <kbd></kbd></span>
><kbd>Alt</kbd> <kbd>Shift</kbd> <kbd></kbd></span
>
<span v-else><kbd></kbd> <kbd></kbd> <kbd></kbd></span> <span v-else><kbd></kbd> <kbd></kbd> <kbd></kbd></span>
</div> </div>
<div class="description"> <div class="description">
@ -226,8 +217,8 @@
</div> </div>
<div class="description"> <div class="description">
<p> <p>
Close current contextual window (context menu, image Close current contextual window (context menu, image viewer, topic edit,
viewer, topic edit, etc) and remove focus from input. etc) and remove focus from input.
</p> </p>
</div> </div>
</div> </div>
@ -241,17 +232,15 @@
</div> </div>
<div class="description"> <div class="description">
<p> <p>
Mark any text typed after this shortcut to be colored. Mark any text typed after this shortcut to be colored. After hitting this
After hitting this shortcut, enter an integer in the shortcut, enter an integer in the range
range <code>015</code> to select the desired color, or use the autocompletion
<code>015</code> to select the desired color, or use menu to choose a color name (see below).
the autocompletion menu to choose a color name (see
below).
</p> </p>
<p> <p>
Background color can be specified by putting a comma and Background color can be specified by putting a comma and another integer in
another integer in the range <code>015</code> after the the range <code>015</code> after the foreground color number
foreground color number (autocompletion works too). (autocompletion works too).
</p> </p>
<p> <p>
A color reference can be found A color reference can be found
@ -337,8 +326,8 @@
</div> </div>
<div class="description"> <div class="description">
<p> <p>
Mark all text typed after this shortcut to be reset to Mark all text typed after this shortcut to be reset to its original
its original formatting. formatting.
</p> </p>
</div> </div>
</div> </div>
@ -346,11 +335,10 @@
<h2>Autocompletion</h2> <h2>Autocompletion</h2>
<p> <p>
To auto-complete nicknames, channels and commands, type one of To auto-complete nicknames, channels, commands, and emoji, type one of the
the characters below to open a suggestion list. Use the characters below to open a suggestion list. Use the <kbd></kbd> and
<kbd></kbd> and <kbd></kbd> keys to highlight an item, and <kbd></kbd> keys to highlight an item, and insert it by pressing <kbd>Tab</kbd> or
insert it by pressing <kbd>Tab</kbd> or <kbd>Enter</kbd> (or by <kbd>Enter</kbd> (or by clicking the desired item).
clicking the desired item).
</p> </p>
<p>Autocompletion can be disabled in settings.</p> <p>Autocompletion can be disabled in settings.</p>
@ -381,6 +369,18 @@
</div> </div>
</div> </div>
<div class="help-item">
<div class="subject">
<code>:</code>
</div>
<div class="description">
<p>
Emoji (note: requires two search characters, to avoid conflicting with
common emoticons like <code>:)</code>)
</p>
</div>
</div>
<h2>Commands</h2> <h2>Commands</h2>
<div class="help-item"> <div class="help-item">
@ -397,9 +397,7 @@
<code>/back</code> <code>/back</code>
</div> </div>
<div class="description"> <div class="description">
<p> <p>Remove your away status (set with <code>/away</code>).</p>
Remove your away status (set with <code>/away</code>).
</p>
</div> </div>
</div> </div>
@ -409,8 +407,8 @@
</div> </div>
<div class="description"> <div class="description">
<p> <p>
Ban (<code>+b</code>) a user from the current channel. Ban (<code>+b</code>) a user from the current channel. This can be a
This can be a nickname or a hostmask. nickname or a hostmask.
</p> </p>
</div> </div>
</div> </div>
@ -430,8 +428,7 @@
</div> </div>
<div class="description"> <div class="description">
<p> <p>
Collapse all previews in the current channel (opposite Collapse all previews in the current channel (opposite of
of
<code>/expand</code>) <code>/expand</code>)
</p> </p>
</div> </div>
@ -443,9 +440,8 @@
</div> </div>
<div class="description"> <div class="description">
<p> <p>
Connect to a new IRC network. If Connect to a new IRC network. If <code>port</code> starts with a
<code>port</code> starts with a <code>+</code> sign, the <code>+</code> sign, the connection will be made secure using TLS.
connection will be made secure using TLS.
</p> </p>
<p>Alias: <code>/server</code></p> <p>Alias: <code>/server</code></p>
</div> </div>
@ -457,8 +453,7 @@
</div> </div>
<div class="description"> <div class="description">
<p> <p>
Send a Send a <abbr title="Client-to-client protocol">CTCP</abbr>
<abbr title="Client-to-client protocol">CTCP</abbr>
request. Read more about this on request. Read more about this on
<a <a
href="https://en.wikipedia.org/wiki/Client-to-client_protocol" href="https://en.wikipedia.org/wiki/Client-to-client_protocol"
@ -476,8 +471,8 @@
</div> </div>
<div class="description"> <div class="description">
<p> <p>
Remove op (<code>-o</code>) from one or several users in Remove op (<code>-o</code>) from one or several users in the current
the current channel. channel.
</p> </p>
</div> </div>
</div> </div>
@ -488,8 +483,8 @@
</div> </div>
<div class="description"> <div class="description">
<p> <p>
Remove voice (<code>-v</code>) from one or several users Remove voice (<code>-v</code>) from one or several users in the current
in the current channel. channel.
</p> </p>
</div> </div>
</div> </div>
@ -499,10 +494,7 @@
<code>/disconnect [message]</code> <code>/disconnect [message]</code>
</div> </div>
<div class="description"> <div class="description">
<p> <p>Disconnect from the current network with an optionally-provided message.</p>
Disconnect from the current network with an
optionally-provided message.
</p>
</div> </div>
</div> </div>
@ -525,8 +517,8 @@
<div class="description"> <div class="description">
<p> <p>
Invite a user to the specified channel. If Invite a user to the specified channel. If
<code>channel</code> is omitted, user will be invited to <code>channel</code> is omitted, user will be invited to the current
the current channel. channel.
</p> </p>
</div> </div>
</div> </div>
@ -537,8 +529,8 @@
</div> </div>
<div class="description"> <div class="description">
<p> <p>
Block any messages from the specified user on the Block any messages from the specified user on the current network. This can
current network. This can be a nickname or a hostmask. be a nickname or a hostmask.
</p> </p>
</div> </div>
</div> </div>
@ -548,9 +540,7 @@
<code>/ignorelist</code> <code>/ignorelist</code>
</div> </div>
<div class="description"> <div class="description">
<p> <p>Load the list of ignored users for the current network.</p>
Load the list of ignored users for the current network.
</p>
</div> </div>
</div> </div>
@ -560,8 +550,8 @@
</div> </div>
<div class="description"> <div class="description">
<p> <p>
Join a channel. Password is only needed in protected Join a channel. Password is only needed in protected channels and can
channels and can usually be omitted. usually be omitted.
</p> </p>
</div> </div>
</div> </div>
@ -581,10 +571,8 @@
</div> </div>
<div class="description"> <div class="description">
<p> <p>
Kick and ban (<code>+b</code>) a user from the current Kick and ban (<code>+b</code>) a user from the current channel. Unlike
channel. Unlike <code>/ban</code>, only nicknames (and not host masks) can be used.
<code>/ban</code>, only nicknames (and not host masks)
can be used.
</p> </p>
</div> </div>
</div> </div>
@ -594,9 +582,7 @@
<code>/list</code> <code>/list</code>
</div> </div>
<div class="description"> <div class="description">
<p> <p>Retrieve a list of available channels on this network.</p>
Retrieve a list of available channels on this network.
</p>
</div> </div>
</div> </div>
@ -606,9 +592,8 @@
</div> </div>
<div class="description"> <div class="description">
<p> <p>
Send an action message to the current channel. Hard Send an action message to the current channel. Hard Lounge will display it
Lounge will display it inline, as if the message was inline, as if the message was posted in the third person.
posted in the third person.
</p> </p>
</div> </div>
</div> </div>
@ -619,10 +604,9 @@
</div> </div>
<div class="description"> <div class="description">
<p> <p>
Set the given flags to the current channel if the active Set the given flags to the current channel if the active window is a
window is a channel, another user if the active window channel, another user if the active window is a private message window, or
is a private message window, or yourself if the current yourself if the current window is a server window.
window is a server window.
</p> </p>
</div> </div>
</div> </div>
@ -642,12 +626,10 @@
</div> </div>
<div class="description"> <div class="description">
<p> <p>
Prevent messages from generating any feedback for a Prevent messages from generating any feedback for a channel. This turns off
channel. This turns off the highlight indicator, hides the highlight indicator, hides mentions and inhibits push notifications.
mentions and inhibits push notifications. Muting a Muting a network lobby mutes the entire network. Not specifying any channel
network lobby mutes the entire network. Not specifying target mutes the current channel. Revert with <code>/unmute</code>.
any channel target mutes the current channel. Revert
with <code>/unmute</code>.
</p> </p>
</div> </div>
</div> </div>
@ -675,10 +657,7 @@
<code>/op nick [...nick]</code> <code>/op nick [...nick]</code>
</div> </div>
<div class="description"> <div class="description">
<p> <p>Give op (<code>+o</code>) to one or several users in the current channel.</p>
Give op (<code>+o</code>) to one or several users in the
current channel.
</p>
</div> </div>
</div> </div>
@ -688,9 +667,8 @@
</div> </div>
<div class="description"> <div class="description">
<p> <p>
Close the specified channel or private message window, Close the specified channel or private message window, or the current
or the current channel if <code>channel</code> is channel if <code>channel</code> is omitted.
omitted.
</p> </p>
<p>Aliases: <code>/close</code>, <code>/leave</code></p> <p>Aliases: <code>/close</code>, <code>/leave</code></p>
</div> </div>
@ -702,9 +680,8 @@
</div> </div>
<div class="description"> <div class="description">
<p> <p>
Leave and immediately rejoin the current channel. Useful Leave and immediately rejoin the current channel. Useful to quickly get op
to quickly get op from ChanServ in an empty channel, for from ChanServ in an empty channel, for example.
example.
</p> </p>
<p>Alias: <code>/cycle</code></p> <p>Alias: <code>/cycle</code></p>
</div> </div>
@ -724,10 +701,7 @@
<code>/quit [message]</code> <code>/quit [message]</code>
</div> </div>
<div class="description"> <div class="description">
<p> <p>Disconnect from the current network with an optional message.</p>
Disconnect from the current network with an optional
message.
</p>
</div> </div>
</div> </div>
@ -765,9 +739,8 @@
</div> </div>
<div class="description"> <div class="description">
<p> <p>
Get the topic in the current channel. If Get the topic in the current channel. If <code>newtopic</code> is specified,
<code>newtopic</code> is specified, sets the topic in sets the topic in the current channel.
the current channel.
</p> </p>
</div> </div>
</div> </div>
@ -778,8 +751,8 @@
</div> </div>
<div class="description"> <div class="description">
<p> <p>
Unban (<code>-b</code>) a user from the current channel. Unban (<code>-b</code>) a user from the current channel. This can be a
This can be a nickname or a hostmask. nickname or a hostmask.
</p> </p>
</div> </div>
</div> </div>
@ -790,8 +763,8 @@
</div> </div>
<div class="description"> <div class="description">
<p> <p>
Unblock messages from the specified user on the current Unblock messages from the specified user on the current network. This can be
network. This can be a nickname or a hostmask. a nickname or a hostmask.
</p> </p>
</div> </div>
</div> </div>
@ -802,9 +775,8 @@
</div> </div>
<div class="description"> <div class="description">
<p> <p>
Un-mutes the given channel(s) or the current channel if Un-mutes the given channel(s) or the current channel if no channel is
no channel is provided. See <code>/mute</code> for more provided. See <code>/mute</code> for more information.
information.
</p> </p>
</div> </div>
</div> </div>
@ -815,8 +787,7 @@
</div> </div>
<div class="description"> <div class="description">
<p> <p>
Give voice (<code>+v</code>) to one or several users in Give voice (<code>+v</code>) to one or several users in the current channel.
the current channel.
</p> </p>
</div> </div>
</div> </div>
@ -826,10 +797,7 @@
<code>/whois nick</code> <code>/whois nick</code>
</div> </div>
<div class="description"> <div class="description">
<p> <p>Retrieve information about the given user on the current network.</p>
Retrieve information about the given user on the current
network.
</p>
</div> </div>
</div> </div>
@ -1225,8 +1193,8 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, ref } from "vue"; import {defineComponent, ref} from "vue";
import { useStore } from "../../js/store"; import {useStore} from "../../js/store";
import SidebarToggle from "../SidebarToggle.vue"; import SidebarToggle from "../SidebarToggle.vue";
import VersionChecker from "../VersionChecker.vue"; import VersionChecker from "../VersionChecker.vue";
@ -1238,8 +1206,7 @@ export default defineComponent({
}, },
setup() { setup() {
const store = useStore(); const store = useStore();
const isApple = const isApple = navigator.platform.match(/(Mac|iPhone|iPod|iPad)/i) || false;
navigator.platform.match(/(Mac|iPhone|iPod|iPad)/i) || false;
const isTouch = navigator.maxTouchPoints > 0; const isTouch = navigator.maxTouchPoints > 0;
return { return {

View File

@ -1,15 +1,35 @@
import constants from "./constants"; import constants from "./constants";
import Mousetrap from "mousetrap"; import Mousetrap from "mousetrap";
import { Strategy, Textcomplete, StrategyProps } from "@textcomplete/core"; import {Strategy, Textcomplete, StrategyProps} from "@textcomplete/core";
import { TextareaEditor } from "@textcomplete/textarea"; import {TextareaEditor} from "@textcomplete/textarea";
import fuzzy from "fuzzy"; import fuzzy from "fuzzy";
import { store } from "./store"; import emojiMap from "./helpers/simplemap.json";
import {store} from "./store";
export default enableAutocomplete; export default enableAutocomplete;
const emojiSearchTerms = Object.keys(emojiMap);
const emojiStrategy: StrategyProps = {
id: "emoji",
match: /(^|\s):([-+\w:?]{2,}):?$/,
search(term: string, callback: (matches) => void) {
// Trim colon from the matched term,
// as we are unable to get a clean string from match regex
term = term.replace(/:$/, "");
callback(fuzzyGrep(term, emojiSearchTerms));
},
template([string, original]: [string, string]) {
return `<span class="emoji">${String(emojiMap[original])}</span> ${string}`;
},
replace([, original]: [string, string]) {
return "$1" + String(emojiMap[original]);
},
index: 2,
};
const nicksStrategy: StrategyProps = { const nicksStrategy: StrategyProps = {
id: "nicks", id: "nicks",
match: /(^|\s)(@([a-zA-Z_[\]\\^{}|`@][a-zA-Z0-9_[\]\\^{}|`-]*)?)$/, match: /(^|\s)(@([a-zA-Z_[\]\\^{}|`@][a-zA-Z0-9_[\]\\^{}|`-]*)?)$/,
@ -19,12 +39,7 @@ const nicksStrategy: StrategyProps = {
if (term[0] === "@") { if (term[0] === "@") {
// TODO: type // TODO: type
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
callback( callback(completeNicks(term.slice(1), true).map((val) => ["@" + val[0], "@" + val[1]]));
completeNicks(term.slice(1), true).map((val) => [
"@" + val[0],
"@" + val[1],
])
);
} else { } else {
callback(completeNicks(term, true)); callback(completeNicks(term, true));
} }
@ -93,9 +108,7 @@ const foregroundColorStrategy: StrategyProps = {
callback(matchingColorCodes); callback(matchingColorCodes);
}, },
template(value: string[]) { template(value: string[]) {
return `<span class="irc-fg${parseInt(value[0], 10)}">${ return `<span class="irc-fg${parseInt(value[0], 10)}">${value[1]}</span>`;
value[1]
}</span>`;
}, },
replace(value: string) { replace(value: string) {
return "\x03" + value[0]; return "\x03" + value[0];
@ -106,11 +119,7 @@ const foregroundColorStrategy: StrategyProps = {
const backgroundColorStrategy: StrategyProps = { const backgroundColorStrategy: StrategyProps = {
id: "background-colors", id: "background-colors",
match: /\x03(\d{2}),(\d{0,2}|[A-Za-z ]{0,10})$/, match: /\x03(\d{2}),(\d{0,2}|[A-Za-z ]{0,10})$/,
search( search(term: string, callback: (matchingColorCodes: string[][]) => void, match: string[]) {
term: string,
callback: (matchingColorCodes: string[][]) => void,
match: string[]
) {
term = term.toLowerCase(); term = term.toLowerCase();
const matchingColorCodes = constants.colorCodeMap const matchingColorCodes = constants.colorCodeMap
.filter((i) => fuzzy.test(term, i[0]) || fuzzy.test(term, i[1])) .filter((i) => fuzzy.test(term, i[0]) || fuzzy.test(term, i[1]))
@ -132,10 +141,10 @@ const backgroundColorStrategy: StrategyProps = {
callback(matchingColorCodes); callback(matchingColorCodes);
}, },
template(value: string[]) { template(value: string[]) {
return `<span class="irc-fg${parseInt( return `<span class="irc-fg${parseInt(value[2], 10)} irc-bg irc-bg${parseInt(
value[2], value[0],
10 10
)} irc-bg irc-bg${parseInt(value[0], 10)}">${value[1]}</span>`; )}">${value[1]}</span>`;
}, },
replace(value: string[]) { replace(value: string[]) {
return "\x03$1," + value[0]; return "\x03$1," + value[0];
@ -170,9 +179,7 @@ function enableAutocomplete(input: HTMLTextAreaElement) {
const text = input.value; const text = input.value;
if (tabCount === 0) { if (tabCount === 0) {
lastMatch = lastMatch = text.substring(0, input.selectionStart).split(/\s/).pop() || "";
text.substring(0, input.selectionStart).split(/\s/).pop() ||
"";
if (lastMatch.length === 0) { if (lastMatch.length === 0) {
return; return;
@ -212,6 +219,7 @@ function enableAutocomplete(input: HTMLTextAreaElement) {
); );
const strategies = [ const strategies = [
emojiStrategy,
nicksStrategy, nicksStrategy,
chanStrategy, chanStrategy,
commandStrategy, commandStrategy,
@ -253,10 +261,7 @@ function replaceNick(original: string, position = 1) {
} }
// If there is whitespace in the input already, append space to nick // If there is whitespace in the input already, append space to nick
if ( if (position > 0 && /\s/.test(store.state.activeChannel?.channel.pendingMessage || "")) {
position > 0 &&
/\s/.test(store.state.activeChannel?.channel.pendingMessage || "")
) {
return original + " "; return original + " ";
} }
@ -280,19 +285,14 @@ function rawNicks() {
if (store.state.activeChannel.channel.users.length > 0) { if (store.state.activeChannel.channel.users.length > 0) {
const users = store.state.activeChannel.channel.users.slice(); const users = store.state.activeChannel.channel.users.slice();
return users return users.sort((a, b) => b.lastMessage - a.lastMessage).map((u) => u.nick);
.sort((a, b) => b.lastMessage - a.lastMessage)
.map((u) => u.nick);
} }
const me = store.state.activeChannel.network.nick; const me = store.state.activeChannel.network.nick;
const otherUser = store.state.activeChannel.channel.name; const otherUser = store.state.activeChannel.channel.name;
// If this is a query, add their name to autocomplete // If this is a query, add their name to autocomplete
if ( if (me !== otherUser && store.state.activeChannel.channel.type === "query") {
me !== otherUser &&
store.state.activeChannel.channel.type === "query"
) {
return [otherUser, me]; return [otherUser, me];
} }

View File

@ -1,7 +1,7 @@
services: services:
hardlounge: hardlounge:
image: git.supernets.org/supernets/hardlounge:latest image: git.supernets.org/supernets/hardlounge:latest
build: . #build: .
ports: ports:
- "9000:9000" - "9000:9000"
volumes: volumes:

View File

@ -1,4 +1,4 @@
import { expect } from "chai"; import {expect} from "chai";
import fs from "fs"; import fs from "fs";
import path from "path"; import path from "path";
@ -6,102 +6,68 @@ describe("public folder", function () {
const publicFolder = path.join(__dirname, "..", "..", "public"); const publicFolder = path.join(__dirname, "..", "..", "public");
it("font awesome files are copied", function () { it("font awesome files are copied", function () {
expect( expect(fs.existsSync(path.join(publicFolder, "fonts", "fa-solid-900.woff"))).to.be.true;
fs.existsSync(path.join(publicFolder, "fonts", "fa-solid-900.woff")) expect(fs.existsSync(path.join(publicFolder, "fonts", "fa-solid-900.woff2"))).to.be.true;
).to.be.true;
expect(
fs.existsSync(
path.join(publicFolder, "fonts", "fa-solid-900.woff2")
)
).to.be.true;
}); });
it("files in root folder are copied", function () { it("files in root folder are copied", function () {
expect(fs.existsSync(path.join(publicFolder, "favicon.ico"))).to.be expect(fs.existsSync(path.join(publicFolder, "favicon.ico"))).to.be.true;
.true;
expect(fs.existsSync(path.join(publicFolder, "robots.txt"))).to.be.true; expect(fs.existsSync(path.join(publicFolder, "robots.txt"))).to.be.true;
expect(fs.existsSync(path.join(publicFolder, "service-worker.js"))).to expect(fs.existsSync(path.join(publicFolder, "service-worker.js"))).to.be.true;
.be.true; expect(fs.existsSync(path.join(publicFolder, "thelounge.webmanifest"))).to.be.true;
expect(fs.existsSync(path.join(publicFolder, "thelounge.webmanifest")))
.to.be.true;
}); });
it("audio files are copied", function () { it("audio files are copied", function () {
expect(fs.existsSync(path.join(publicFolder, "audio", "pop.wav"))).to.be expect(fs.existsSync(path.join(publicFolder, "audio", "pop.wav"))).to.be.true;
.true;
}); });
it("index HTML file is not copied", function () { it("index HTML file is not copied", function () {
expect(fs.existsSync(path.join(publicFolder, "index.html"))).to.be expect(fs.existsSync(path.join(publicFolder, "index.html"))).to.be.false;
.false; expect(fs.existsSync(path.join(publicFolder, "index.html.tpl"))).to.be.false;
expect(fs.existsSync(path.join(publicFolder, "index.html.tpl"))).to.be
.false;
}); });
it("javascript files are built", function () { it("javascript files are built", function () {
expect(fs.existsSync(path.join(publicFolder, "js", "bundle.js"))).to.be expect(fs.existsSync(path.join(publicFolder, "js", "bundle.js"))).to.be.true;
.true; expect(fs.existsSync(path.join(publicFolder, "js", "bundle.vendor.js"))).to.be.true;
expect(fs.existsSync(path.join(publicFolder, "js", "bundle.vendor.js")))
.to.be.true;
}); });
it("style files are built", function () { it("style files are built", function () {
expect(fs.existsSync(path.join(publicFolder, "css", "style.css"))).to.be expect(fs.existsSync(path.join(publicFolder, "css", "style.css"))).to.be.true;
.true; expect(fs.existsSync(path.join(publicFolder, "css", "style.css.map"))).to.be.true;
expect(fs.existsSync(path.join(publicFolder, "css", "style.css.map"))) expect(fs.existsSync(path.join(publicFolder, "themes", "default.css"))).to.be.true;
.to.be.true; expect(fs.existsSync(path.join(publicFolder, "themes", "morning.css"))).to.be.true;
expect(fs.existsSync(path.join(publicFolder, "themes", "default.css")))
.to.be.true;
expect(fs.existsSync(path.join(publicFolder, "themes", "morning.css")))
.to.be.true;
expect(fs.existsSync(path.join(publicFolder, "themes", "oled.css"))).to
.be.true;
}); });
it("style files contain expected content", function (done) { it("style files contain expected content", function (done) {
fs.readFile( fs.readFile(path.join(publicFolder, "css", "style.css"), "utf8", function (err, contents) {
path.join(publicFolder, "css", "style.css"),
"utf8",
function (err, contents) {
expect(err).to.be.null; expect(err).to.be.null;
expect(contents.includes("var(--body-color)")).to.be.true; expect(contents.includes("var(--body-color)")).to.be.true;
expect(contents.includes("url(../fonts/fa-solid-900.woff2)")).to expect(contents.includes("url(../fonts/fa-solid-900.woff2)")).to.be.true;
.be.true; expect(contents.includes(".tooltipped{position:relative}")).to.be.true;
expect(contents.includes(".tooltipped{position:relative}")).to
.be.true;
expect(contents.includes("sourceMappingURL")).to.be.true; expect(contents.includes("sourceMappingURL")).to.be.true;
done(); done();
} });
);
}); });
it("javascript map is created", function () { it("javascript map is created", function () {
expect(fs.existsSync(path.join(publicFolder, "js", "bundle.js.map"))).to expect(fs.existsSync(path.join(publicFolder, "js", "bundle.js.map"))).to.be.true;
.be.true;
}); });
it("loading-error-handlers.js is copied", function () { it("loading-error-handlers.js is copied", function () {
expect( expect(fs.existsSync(path.join(publicFolder, "js", "loading-error-handlers.js"))).to.be
fs.existsSync( .true;
path.join(publicFolder, "js", "loading-error-handlers.js")
)
).to.be.true;
}); });
it("service worker has cacheName set", function (done) { it("service worker has cacheName set", function (done) {
fs.readFile( fs.readFile(path.join(publicFolder, "service-worker.js"), "utf8", function (err, contents) {
path.join(publicFolder, "service-worker.js"),
"utf8",
function (err, contents) {
expect(err).to.be.null; expect(err).to.be.null;
expect(contents.includes("const cacheName")).to.be.true; expect(contents.includes("const cacheName")).to.be.true;
expect(contents.includes("__HASH__")).to.be.false; expect(contents.includes("__HASH__")).to.be.false;
done(); done();
} });
);
}); });
}); });