mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-02-12 10:00:36 +01:00
Merge branch 'staging' into parser-followup-2
This commit is contained in:
commit
6d003cf140
43
package-lock.json
generated
43
package-lock.json
generated
@ -165,19 +165,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@eslint/eslintrc/node_modules/strip-json-comments": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
|
||||
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "8.55.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz",
|
||||
@ -2445,9 +2432,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
|
||||
"integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
@ -3009,10 +2996,13 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"license": "MIT"
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
@ -5085,6 +5075,19 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-json-comments": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
|
||||
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/strtok3": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz",
|
||||
|
@ -55,16 +55,11 @@
|
||||
|
||||
/* Flashing for highlighting animation */
|
||||
@keyframes flash {
|
||||
|
||||
20%,
|
||||
60%,
|
||||
100% {
|
||||
0%, 50%, 100% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
0%,
|
||||
40%,
|
||||
80% {
|
||||
25%, 75% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,10 @@ body.tts .mes_narrate {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
body:not(.tts) #ttsExtensionNarrateAll {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body.no-hotswap .hotswap,
|
||||
body.no-timer .mes_timer,
|
||||
body.no-timestamps .timestamp,
|
||||
|
@ -2485,19 +2485,21 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="ReverseProxyWarningMessage" class="reverse_proxy_warning">
|
||||
<b>
|
||||
<div data-i18n="Using a proxy that you're not running yourself is a risk to your data privacy.">
|
||||
Using a proxy that you're not running yourself is a risk to your data privacy.
|
||||
</div>
|
||||
<div data-i18n="ANY support requests will be REFUSED if you are using a proxy.">
|
||||
ANY support requests will be REFUSED if you are using a proxy.
|
||||
</div>
|
||||
<hr>
|
||||
<i data-i18n="Do not proceed if you do not agree to this!">
|
||||
Do not proceed if you do not agree to this!
|
||||
</i>
|
||||
</b>
|
||||
<div id="ReverseProxyWarningMessage" data-source="openai,claude,mistralai,makersuite">
|
||||
<div class="reverse_proxy_warning">
|
||||
<b>
|
||||
<div data-i18n="Using a proxy that you're not running yourself is a risk to your data privacy.">
|
||||
Using a proxy that you're not running yourself is a risk to your data privacy.
|
||||
</div>
|
||||
<div data-i18n="ANY support requests will be REFUSED if you are using a proxy.">
|
||||
ANY support requests will be REFUSED if you are using a proxy.
|
||||
</div>
|
||||
<hr>
|
||||
<i data-i18n="Do not proceed if you do not agree to this!">
|
||||
Do not proceed if you do not agree to this!
|
||||
</i>
|
||||
</b>
|
||||
</div>
|
||||
</div>
|
||||
<form id="openai_form" data-source="openai" action="javascript:void(null);" method="post" enctype="multipart/form-data">
|
||||
<h4><span data-i18n="OpenAI API key">OpenAI API key</span></h4>
|
||||
|
@ -908,13 +908,13 @@ async function firstLoadInit() {
|
||||
await getClientVersion();
|
||||
await readSecretState();
|
||||
initLocales();
|
||||
initDefaultSlashCommands();
|
||||
await getSystemMessages();
|
||||
sendSystemMessage(system_message_types.WELCOME);
|
||||
await getSettings();
|
||||
initKeyboard();
|
||||
initDynamicStyles();
|
||||
initTags();
|
||||
initDefaultSlashCommands();
|
||||
await getUserAvatars(true, user_avatar);
|
||||
await getCharacters();
|
||||
await getBackgrounds();
|
||||
|
@ -1,9 +0,0 @@
|
||||
<div class="m-b-1">
|
||||
Are you sure you want to connect to '{{url}}'?
|
||||
</div>
|
||||
<div class="flex-container justifyCenter">
|
||||
<label class="checkbox_label" for="assets-remember">
|
||||
<input type="checkbox" id="assets-remember">
|
||||
Don't ask again for this URL
|
||||
</label>
|
||||
</div>
|
@ -3,11 +3,11 @@ TODO:
|
||||
*/
|
||||
//const DEBUG_TONY_SAMA_FORK_MODE = true
|
||||
|
||||
import { getRequestHeaders, callPopup, processDroppedFiles, eventSource, event_types } from '../../../script.js';
|
||||
import { getRequestHeaders, processDroppedFiles, eventSource, event_types } from '../../../script.js';
|
||||
import { deleteExtension, extensionNames, getContext, installExtension, renderExtensionTemplateAsync } from '../../extensions.js';
|
||||
import { POPUP_TYPE, callGenericPopup } from '../../popup.js';
|
||||
import { POPUP_TYPE, Popup, callGenericPopup } from '../../popup.js';
|
||||
import { executeSlashCommands } from '../../slash-commands.js';
|
||||
import { getStringHash, isValidUrl } from '../../utils.js';
|
||||
import { flashHighlight, getStringHash, isValidUrl } from '../../utils.js';
|
||||
export { MODULE_NAME };
|
||||
|
||||
const MODULE_NAME = 'assets';
|
||||
@ -219,6 +219,12 @@ function downloadAssetsList(url) {
|
||||
$('#assets_menu').show();
|
||||
})
|
||||
.catch((error) => {
|
||||
// Info hint if the user maybe... likely accidently was trying to install an extension and we wanna help guide them? uwu :3
|
||||
const installButton = $('#third_party_extension_button');
|
||||
flashHighlight(installButton, 10_000);
|
||||
toastr.info('Click the flashing button at the top right corner of the menu.', 'Trying to install a custom extension?', { timeOut: 10_000 });
|
||||
|
||||
// Error logged after, to appear on top
|
||||
console.error(error);
|
||||
toastr.error('Problem with assets URL', DEBUG_PREFIX + 'Cannot get assets list');
|
||||
$('#assets-connect-button').addClass('fa-plug-circle-exclamation');
|
||||
@ -405,22 +411,31 @@ jQuery(async () => {
|
||||
openCharacterBrowser(false);
|
||||
});
|
||||
|
||||
const installHintButton = windowHtml.find('.assets-install-hint-link');
|
||||
installHintButton.on('click', async function () {
|
||||
const installButton = $('#third_party_extension_button');
|
||||
flashHighlight(installButton, 5000);
|
||||
toastr.info('Click the flashing button to install extensions.', 'How to install extensions?');
|
||||
});
|
||||
|
||||
const connectButton = windowHtml.find('#assets-connect-button');
|
||||
connectButton.on('click', async function () {
|
||||
const url = String(assetsJsonUrl.val());
|
||||
const url = DOMPurify.sanitize(String(assetsJsonUrl.val()));
|
||||
const rememberKey = `Assets_SkipConfirm_${getStringHash(url)}`;
|
||||
const skipConfirm = localStorage.getItem(rememberKey) === 'true';
|
||||
|
||||
const template = await renderExtensionTemplateAsync(MODULE_NAME, 'confirm', { url });
|
||||
const confirmation = skipConfirm || await callPopup(template, 'confirm');
|
||||
const confirmation = skipConfirm || await Popup.show.confirm('Loading Asset List', `<span>Are you sure you want to connect to the following url?</span><var>${url}</var>`, {
|
||||
customInputs: [{ id: 'assets-remember', label: 'Don\'t ask again for this URL' }],
|
||||
onClose: popup => {
|
||||
if (popup.result) {
|
||||
const rememberValue = popup.inputResults.get('assets-remember');
|
||||
localStorage.setItem(rememberKey, String(rememberValue));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (confirmation) {
|
||||
try {
|
||||
if (!skipConfirm) {
|
||||
const rememberValue = Boolean($('#assets-remember').prop('checked'));
|
||||
localStorage.setItem(rememberKey, String(rememberValue));
|
||||
}
|
||||
|
||||
console.debug(DEBUG_PREFIX, 'Confimation, loading assets...');
|
||||
downloadAssetsList(url);
|
||||
connectButton.removeClass('fa-plug-circle-exclamation');
|
||||
|
@ -7,10 +7,18 @@
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.assets-url-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.assets-install-hint-link {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.assets-connect-div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.assets-list-git {
|
||||
|
@ -5,10 +5,30 @@
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<label for="assets-json-url-field" data-i18n="Assets URL">Assets URL</label>
|
||||
<div class="assets-connect-div">
|
||||
<input id="assets-json-url-field" class="text_pole widthUnset flex1">
|
||||
<i id="assets-connect-button" class="menu_button fa-solid fa-plug-circle-exclamation fa-xl redOverlayGlow"></i>
|
||||
<small>
|
||||
<span data-i18n="Load a custom asset list or select">
|
||||
Load a custom asset list or select
|
||||
</span>
|
||||
<a class="assets-install-hint-link" data-i18n="Install Extension">Install Extension</a>
|
||||
<span data-i18n="to install 3rd party extensions.">
|
||||
to install 3rd party extensions.
|
||||
</span>
|
||||
</small>
|
||||
<div class="assets-url-block m-b-1 m-t-1">
|
||||
<label for="assets-json-url-field" data-i18n="Assets URL">Assets URL</label>
|
||||
<small title="Load a list of extensions & assets based on an asset list file.
|
||||
|
||||
The default Asset URL in this field points to the list of offical first party extensions and assets.
|
||||
If you have a custom asset list, you can insert it here.
|
||||
|
||||
To install a single 3rd party extension, use the "Install Extensions" button on the top right.">
|
||||
<span>Load an asset list</span>
|
||||
<div class="fa-solid fa-circle-info opacity50p"></div>
|
||||
</small>
|
||||
<div class="assets-connect-div">
|
||||
<input id="assets-json-url-field" class="text_pole widthUnset flex1">
|
||||
<i id="assets-connect-button" class="menu_button fa-solid fa-plug-circle-exclamation fa-xl redOverlayGlow" title="Load Asset List" data-i18n="[title]Load Asset List"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div id="assets_filters" class="flex-container">
|
||||
<select id="assets_type_select" class="text_pole flex1">
|
||||
|
@ -3353,7 +3353,7 @@ async function addSDGenButtons() {
|
||||
|
||||
$(document).on('click touchend', function (e) {
|
||||
const target = $(e.target);
|
||||
if (target.is(dropdown)) return;
|
||||
if (target.is(dropdown) || target.closest(dropdown).length) return;
|
||||
if (target.is(button) && !dropdown.is(':visible') && $('#send_but').is(':visible')) {
|
||||
e.preventDefault();
|
||||
|
||||
@ -3365,6 +3365,7 @@ async function addSDGenButtons() {
|
||||
});
|
||||
|
||||
$('#sd_dropdown [id]').on('click', function () {
|
||||
dropdown.fadeOut(animation_duration);
|
||||
const id = $(this).attr('id');
|
||||
const idParamMap = {
|
||||
'sd_you': 'you',
|
||||
|
@ -348,13 +348,18 @@ function onAudioControlClicked() {
|
||||
}
|
||||
|
||||
function addAudioControl() {
|
||||
|
||||
$('#tts_wand_container').append(`
|
||||
<div id="ttsExtensionMenuItem" class="list-group-item flex-container flexGap5">
|
||||
<div id="tts_media_control" class="extensionsMenuExtensionButton "/></div>
|
||||
TTS Playback
|
||||
</div>`);
|
||||
$('#tts_wand_container').append(`
|
||||
<div id="ttsExtensionNarrateAll" class="list-group-item flex-container flexGap5">
|
||||
<div class="extensionsMenuExtensionButton fa-solid fa-radio"></div>
|
||||
Narrate All Chat
|
||||
</div>`);
|
||||
$('#ttsExtensionMenuItem').attr('title', 'TTS play/pause').on('click', onAudioControlClicked);
|
||||
$('#ttsExtensionNarrateAll').attr('title', 'Narrate all messages in the current chat. Includes user messages, excludes hidden comments.').on('click', playFullConversation);
|
||||
updateUiAudioPlayState();
|
||||
}
|
||||
|
||||
@ -512,12 +517,23 @@ async function processTtsQueue() {
|
||||
}
|
||||
}
|
||||
|
||||
// Secret function for now
|
||||
async function playFullConversation() {
|
||||
resetTtsPlayback();
|
||||
|
||||
if (!extension_settings.tts.enabled) {
|
||||
return toastr.warning('TTS is disabled. Please enable it in the extension settings.');
|
||||
}
|
||||
|
||||
const context = getContext();
|
||||
const chat = context.chat;
|
||||
const chat = context.chat.filter(x => !x.is_system && x.mes !== '...' && x.mes !== '');
|
||||
|
||||
if (chat.length === 0) {
|
||||
return toastr.info('No messages to narrate.');
|
||||
}
|
||||
|
||||
ttsJobQueue = chat;
|
||||
}
|
||||
|
||||
window['playFullConversation'] = playFullConversation;
|
||||
|
||||
//#############################//
|
||||
|
@ -4,10 +4,10 @@ import { updateSecretDisplay } from './secrets.js';
|
||||
const storageKey = 'language';
|
||||
const overrideLanguage = localStorage.getItem(storageKey);
|
||||
const localeFile = String(overrideLanguage || navigator.language || navigator.userLanguage || 'en').toLowerCase();
|
||||
const langs = await fetch('/locales/lang.json').then(response => response.json());
|
||||
var langs;
|
||||
// Don't change to let/const! It will break module loading.
|
||||
// eslint-disable-next-line prefer-const
|
||||
var localeData = await getLocaleData(localeFile);
|
||||
var localeData;
|
||||
|
||||
/**
|
||||
* An observer that will check if any new i18n elements are added to the document
|
||||
@ -214,7 +214,9 @@ function addLanguagesToDropdown() {
|
||||
}
|
||||
}
|
||||
|
||||
export function initLocales() {
|
||||
export async function initLocales() {
|
||||
langs = await fetch('/locales/lang.json').then(response => response.json());
|
||||
localeData = await getLocaleData(localeFile);
|
||||
applyLocale();
|
||||
addLanguagesToDropdown();
|
||||
updateSecretDisplay();
|
||||
|
@ -2489,6 +2489,7 @@ async function triggerGenerationCallback(args, value) {
|
||||
} catch {
|
||||
console.warn('Timeout waiting for generation unlock');
|
||||
toastr.warning('Cannot run /trigger command while the reply is being generated.');
|
||||
outerResolve(Promise.resolve(''));
|
||||
return '';
|
||||
}
|
||||
|
||||
|
@ -1571,13 +1571,29 @@ export function setValueByPath(obj, path, value) {
|
||||
/**
|
||||
* Flashes the given HTML element via CSS flash animation for a defined period
|
||||
* @param {JQuery<HTMLElement>} element - The element to flash
|
||||
* @param {number} timespan - A numer in milliseconds how the flash should last
|
||||
* @param {number} timespan - A number in milliseconds how the flash should last (default is 2000ms. Multiples of 1000ms work best, as they end with the flash animation being at 100% opacity)
|
||||
*/
|
||||
export function flashHighlight(element, timespan = 2000) {
|
||||
const flashDuration = 2000; // Duration of a single flash cycle in milliseconds
|
||||
|
||||
element.addClass('flash animated');
|
||||
setTimeout(() => element.removeClass('flash animated'), timespan);
|
||||
element.css('--animation-duration', `${flashDuration}ms`);
|
||||
|
||||
// Repeat the flash animation
|
||||
const intervalId = setInterval(() => {
|
||||
element.removeClass('flash animated');
|
||||
void element[0].offsetWidth; // Trigger reflow to restart animation
|
||||
element.addClass('flash animated');
|
||||
}, flashDuration);
|
||||
|
||||
setTimeout(() => {
|
||||
clearInterval(intervalId);
|
||||
element.removeClass('flash animated');
|
||||
element.css('--animation-duration', '');
|
||||
}, timespan);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the given control has an animation applied to it
|
||||
*
|
||||
|
@ -1912,21 +1912,31 @@ export function registerVariableCommands() {
|
||||
unnamedArgumentList: [
|
||||
SlashCommandArgument.fromProps({
|
||||
description: 'value',
|
||||
typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
|
||||
typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.LIST, ARGUMENT_TYPE.DICTIONARY],
|
||||
isRequired: true,
|
||||
enumProvider: commonEnumProviders.variables('all'),
|
||||
forceEnum: false,
|
||||
}),
|
||||
],
|
||||
helpString: `
|
||||
<div>
|
||||
Gets the length of a value and passes the result down the pipe. Can use variable names.
|
||||
Gets the length of a value and passes the result down the pipe.
|
||||
<ul>
|
||||
<li>
|
||||
For strings, returns the number of characters.
|
||||
</li>
|
||||
<li>
|
||||
For lists and dictionaries, returns the number of elements.
|
||||
</li>
|
||||
<li>
|
||||
For numbers, returns the number of digits (including the sign and decimal point).
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<strong>Example:</strong>
|
||||
<ul>
|
||||
<li>
|
||||
<pre><code class="language-stscript">/len i</code></pre>
|
||||
<pre><code class="language-stscript">/len Lorem ipsum | /echo</code></pre>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -208,8 +208,8 @@ table.responsiveTable {
|
||||
}
|
||||
|
||||
.animated {
|
||||
-webkit-animation-duration: 3s !important;
|
||||
animation-duration: 3s !important;
|
||||
-webkit-animation-duration: var(--animation-duration, 3s) !important;
|
||||
animation-duration: var(--animation-duration, 3s) !important;
|
||||
-webkit-animation-fill-mode: both !important;
|
||||
animation-fill-mode: both !important;
|
||||
box-shadow: inset 0 0 5px var(--SmartThemeQuoteColor);
|
||||
|
@ -40,7 +40,7 @@ router.post('/recognize', jsonParser, async (req, res) => {
|
||||
const pipe = await module.default.getPipeline(TASK, model);
|
||||
const wav = getWaveFile(audio);
|
||||
const start = performance.now();
|
||||
const result = await pipe(wav, { language: lang || null });
|
||||
const result = await pipe(wav, { language: lang || null, task: 'transcribe' });
|
||||
const end = performance.now();
|
||||
console.log(`Execution duration: ${(end - start) / 1000} seconds`);
|
||||
console.log('Transcribed audio:', result.text);
|
||||
|
37
tests/.eslintrc.js
Normal file
37
tests/.eslintrc.js
Normal file
@ -0,0 +1,37 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
plugins: [
|
||||
'jest',
|
||||
],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
],
|
||||
env: {
|
||||
es6: true,
|
||||
node: true,
|
||||
"jest/globals": true,
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
},
|
||||
overrides: [
|
||||
],
|
||||
ignorePatterns: [
|
||||
],
|
||||
rules: {
|
||||
'no-unused-vars': ['error', { args: 'none' }],
|
||||
'no-control-regex': 'off',
|
||||
'no-constant-condition': ['error', { checkLoops: false }],
|
||||
'require-yield': 'off',
|
||||
'quotes': ['error', 'single'],
|
||||
'semi': ['error', 'always'],
|
||||
'indent': ['error', 4, { SwitchCase: 1, FunctionDeclaration: { parameters: 'first' } }],
|
||||
'comma-dangle': ['error', 'always-multiline'],
|
||||
'eol-last': ['error', 'always'],
|
||||
'no-trailing-spaces': 'error',
|
||||
'object-curly-spacing': ['error', 'always'],
|
||||
'space-infix-ops': 'error',
|
||||
'no-unused-expressions': ['error', { allowShortCircuit: true, allowTernary: true }],
|
||||
'no-cond-assign': 'error',
|
||||
},
|
||||
};
|
9
tests/jest.config.json
Normal file
9
tests/jest.config.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"verbose": true,
|
||||
"transform": {},
|
||||
"extensionsToTreatAsEsm": [],
|
||||
"preset": "jest-puppeteer",
|
||||
"setupFilesAfterEnv": [
|
||||
"<rootDir>/jest.setup.js"
|
||||
]
|
||||
}
|
2
tests/jest.setup.js
Normal file
2
tests/jest.setup.js
Normal file
@ -0,0 +1,2 @@
|
||||
// Initialize global variables for Jest tests here
|
||||
global.ST_URL = 'http://localhost:8000';
|
5977
tests/package-lock.json
generated
Normal file
5977
tests/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
15
tests/package.json
Normal file
15
tests/package.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "sillytavern-tests",
|
||||
"type": "module",
|
||||
"license": "AGPL-3.0",
|
||||
"scripts": {
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/jest": "^29.5.12",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-jest": "^28.6.0",
|
||||
"jest": "^29.7.0",
|
||||
"jest-puppeteer": "^10.0.1"
|
||||
}
|
||||
}
|
10
tests/sample.test.js
Normal file
10
tests/sample.test.js
Normal file
@ -0,0 +1,10 @@
|
||||
describe('sample', () => {
|
||||
beforeAll(async () => {
|
||||
await page.goto(global.ST_URL);
|
||||
await page.waitForFunction('document.getElementById("preloader") === null', { timeout: 0 });
|
||||
});
|
||||
|
||||
it('should be titled "SillyTavern"', async () => {
|
||||
await expect(page.title()).resolves.toMatch('SillyTavern');
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user