frontend linting

This commit is contained in:
realaravinth 2021-10-08 15:24:29 +05:30
parent f7afc72d81
commit 53720ff740
No known key found for this signature in database
GPG Key ID: AD9F0F08E855ED88
91 changed files with 2158 additions and 1677 deletions

21
.eslintrc.js Normal file
View File

@ -0,0 +1,21 @@
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaVersion: 12,
sourceType: "module",
},
plugins: ["@typescript-eslint"],
rules: {
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/ban-types": "off",
indent: ["error", 2],
"linebreak-style": ["error", "unix"],
quotes: ["error", "double"],
semi: ["error", "always"],
},
};

View File

@ -80,6 +80,9 @@ jobs:
# - name: build frontend # - name: build frontend
# run: make frontend # run: make frontend
#
- name: lint frontend
run: yarn lint
- name: run tests - name: run tests
run: make test run: make test

View File

@ -4,6 +4,7 @@
"version": "1.0.0", "version": "1.0.0",
"scripts": { "scripts": {
"build": "webpack --mode production", "build": "webpack --mode production",
"lint": "yarn run eslint templates",
"start": "webpack-dev-server --mode development --progress --color", "start": "webpack-dev-server --mode development --progress --color",
"test": "jest" "test": "jest"
}, },
@ -12,10 +13,13 @@
"@types/jsdom": "^16.2.10", "@types/jsdom": "^16.2.10",
"@types/node": "^15.0.2", "@types/node": "^15.0.2",
"@types/sinon": "^10.0.0", "@types/sinon": "^10.0.0",
"@typescript-eslint/eslint-plugin": "^4.31.2",
"@typescript-eslint/parser": "^4.31.2",
"@wasm-tool/wasm-pack-plugin": "^1.4.0", "@wasm-tool/wasm-pack-plugin": "^1.4.0",
"css-loader": "^5.2.4", "css-loader": "^5.2.4",
"css-minimizer-webpack-plugin": "^2.0.0", "css-minimizer-webpack-plugin": "^2.0.0",
"dart-sass": "^1.25.0", "dart-sass": "^1.25.0",
"eslint": "^7.32.0",
"jest": "^26.6.3", "jest": "^26.6.3",
"jest-fetch-mock": "^3.0.3", "jest-fetch-mock": "^3.0.3",
"jsdom": "^16.5.3", "jsdom": "^16.5.3",

View File

@ -17,7 +17,7 @@
use actix_identity::Identity; use actix_identity::Identity;
use actix_web::{web, HttpResponse, Responder}; use actix_web::{web, HttpResponse, Responder};
use futures::future::try_join_all; use futures::future::try_join_all;
use libmcaptcha::{defense::Level, DefenseBuilder, master::messages::RemoveCaptcha}; use libmcaptcha::{defense::Level, master::messages::RemoveCaptcha, DefenseBuilder};
use log::debug; use log::debug;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -190,8 +190,16 @@ async fn update_levels(
} }
try_join_all(futs).await?; try_join_all(futs).await?;
if let Err(ServiceError::CaptchaError(e)) = data.captcha.remove(RemoveCaptcha(payload.key.clone())).await { if let Err(ServiceError::CaptchaError(e)) = data
log::error!("Deleting captcha key {} while updating it, error: {:?}", &payload.key, e) .captcha
.remove(RemoveCaptcha(payload.key.clone()))
.await
{
log::error!(
"Deleting captcha key {} while updating it, error: {:?}",
&payload.key,
e
)
} }
Ok(HttpResponse::Ok()) Ok(HttpResponse::Ok())
} }

View File

@ -16,15 +16,15 @@
*/ */
const ROUTES = { const ROUTES = {
registerUser: '/api/v1/signup', registerUser: "/api/v1/signup",
loginUser: '/api/v1/signin', loginUser: "/api/v1/signin",
signoutUser: '/api/v1/signout', signoutUser: "/api/v1/signout",
deleteAccount: '/api/v1/account/delete', deleteAccount: "/api/v1/account/delete",
usernameExists: '/api/v1/account/username/exists', usernameExists: "/api/v1/account/username/exists",
emailExists: '/api/v1/account/email/exists', emailExists: "/api/v1/account/email/exists",
healthCheck: '/api/v1/meta/health', healthCheck: "/api/v1/meta/health",
buildDetails: '/api/v1/meta/build', buildDetails: "/api/v1/meta/build",
markNotificationRead: '/api/v1/notifications/read', markNotificationRead: "/api/v1/notifications/read",
}; };
export default ROUTES; export default ROUTES;

View File

@ -14,6 +14,6 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import * as lib from 'mcaptcha-glue'; import * as lib from "mcaptcha-glue";
export const register = () => lib.init(); export const register = (): void => lib.init();

View File

@ -14,38 +14,38 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {init} from 'mcaptcha-glue'; import {init} from "mcaptcha-glue";
import VIEWS from '../../../views/v1/routes'; import VIEWS from "../../../views/v1/routes";
import isBlankString from '../../../utils/isBlankString'; import isBlankString from "../../../utils/isBlankString";
import genJsonPayload from '../../../utils/genJsonPayload'; import genJsonPayload from "../../../utils/genJsonPayload";
import getFormUrl from '../../../utils/getFormUrl'; import getFormUrl from "../../../utils/getFormUrl";
import registerShowPassword from '../../../components/showPassword'; import registerShowPassword from "../../../components/showPassword";
import createError from '../../../components/error/index'; import createError from "../../../components/error/index";
//import '../forms.scss'; //import '../forms.scss';
export const getPassword = () => { export const getPassword = (): string | null => {
const passwordElement = <HTMLInputElement>document.getElementById('password'); const passwordElement = <HTMLInputElement>document.getElementById("password");
if (passwordElement === null) { if (passwordElement === null) {
console.debug('Password is null'); console.debug("Password is null");
return; return;
} }
return passwordElement.value; return passwordElement.value;
}; };
const login = async (e: Event) => { const login = async (e: Event): Promise<void> => {
e.preventDefault(); e.preventDefault();
const loginElement = <HTMLInputElement>document.getElementById('login'); const loginElement = <HTMLInputElement>document.getElementById("login");
if (loginElement === null) { if (loginElement === null) {
console.debug('login element element is null'); console.debug("login element element is null");
return; return;
} }
const login = loginElement.value; const login = loginElement.value;
isBlankString(login, 'username', e); isBlankString(login, "username", e);
const password = getPassword(); const password = getPassword();
@ -65,9 +65,9 @@ const login = async (e: Event) => {
} }
}; };
export const index = () => { export const index = (): void => {
const form = <HTMLFontElement>document.getElementById('form'); const form = <HTMLFontElement>document.getElementById("form");
form.addEventListener('submit', login, true); form.addEventListener("submit", login, true);
registerShowPassword(); registerShowPassword();
init(); init();
}; };

View File

@ -15,13 +15,13 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import fetchMock from 'jest-fetch-mock'; import fetchMock from "jest-fetch-mock";
import emailExists from './emailExists'; import emailExists from "./emailExists";
import {mockAlert, getRegistrationFormHtml} from '../../../setUpTests'; import {mockAlert, getRegistrationFormHtml} from "../../../setUpTests";
import setup from '../../../components/error/setUpTests'; import setup from "../../../components/error/setUpTests";
fetchMock.enableMocks(); fetchMock.enableMocks();
mockAlert(); mockAlert();
@ -30,14 +30,14 @@ beforeEach(() => {
fetchMock.resetMocks(); fetchMock.resetMocks();
}); });
it('finds exchange', async () => { it("finds exchange", async () => {
fetchMock.mockResponseOnce(JSON.stringify({exists: true})); fetchMock.mockResponseOnce(JSON.stringify({exists: true}));
document.body.innerHTML = getRegistrationFormHtml(); document.body.innerHTML = getRegistrationFormHtml();
document.querySelector('body').appendChild(setup()); document.querySelector("body").appendChild(setup());
const emailField = <HTMLInputElement>document.getElementById('email'); const emailField = <HTMLInputElement>document.getElementById("email");
emailField.setAttribute('value', 'test@a.com'); emailField.setAttribute("value", "test@a.com");
expect(await emailExists()).toBe(true); expect(await emailExists()).toBe(true);

View File

@ -15,15 +15,15 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import ROUTES from '../../../api/v1/routes'; import ROUTES from "../../../api/v1/routes";
import genJsonPayload from '../../../utils/genJsonPayload'; import genJsonPayload from "../../../utils/genJsonPayload";
import createError from '../../../components/error/index'; import createError from "../../../components/error/index";
const emailExists = async (element?: HTMLInputElement) => { const emailExists = async (element?: HTMLInputElement): Promise<boolean> => {
let email; let email;
if (element === undefined || element === null) { if (element === undefined || element === null) {
email = <HTMLInputElement>document.getElementById('email'); email = <HTMLInputElement>document.getElementById("email");
} else { } else {
email = element; email = element;
} }
@ -37,7 +37,7 @@ const emailExists = async (element?: HTMLInputElement) => {
if (res.ok) { if (res.ok) {
const data = await res.json(); const data = await res.json();
if (data.exists) { if (data.exists) {
email.className += ' form__in-field--warn'; email.className += " form__in-field--warn";
createError(`Email "${val}" is already used`); createError(`Email "${val}" is already used`);
return data.exists; return data.exists;
} }

View File

@ -15,33 +15,33 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import VIEWS from '../../../views/v1/routes'; import VIEWS from "../../../views/v1/routes";
import isBlankString from '../../../utils/isBlankString'; import isBlankString from "../../../utils/isBlankString";
import genJsonPayload from '../../../utils/genJsonPayload'; import genJsonPayload from "../../../utils/genJsonPayload";
import userExists from './userExists'; import userExists from "./userExists";
import emailExists from './emailExists'; import emailExists from "./emailExists";
import getFormUrl from '../../../utils/getFormUrl'; import getFormUrl from "../../../utils/getFormUrl";
import registerShowPassword from '../../../components/showPassword'; import registerShowPassword from "../../../components/showPassword";
import createError from '../../../components/error/index'; import createError from "../../../components/error/index";
//import '../forms.scss'; //import '../forms.scss';
const usernameElement = <HTMLInputElement>document.getElementById('username'); const usernameElement = <HTMLInputElement>document.getElementById("username");
const emailElement = <HTMLInputElement>document.getElementById('email'); const emailElement = <HTMLInputElement>document.getElementById("email");
const passwordElement = <HTMLInputElement>document.getElementById('password'); const passwordElement = <HTMLInputElement>document.getElementById("password");
const registerUser = async (e: Event) => { const registerUser = async (e: Event): Promise<void> => {
e.preventDefault(); e.preventDefault();
const username = usernameElement.value; const username = usernameElement.value;
isBlankString(username, 'username', e); isBlankString(username, "username", e);
//isBlankString(e);//, username, 'username'); //isBlankString(e);//, username, 'username');
const password = passwordElement.value; const password = passwordElement.value;
const passwordCheckElement = <HTMLInputElement>( const passwordCheckElement = <HTMLInputElement>(
document.getElementById('password-check') document.getElementById("password-check")
); );
const passwordCheck = passwordCheckElement.value; const passwordCheck = passwordCheckElement.value;
if (password != passwordCheck) { if (password != passwordCheck) {
@ -54,7 +54,7 @@ const registerUser = async (e: Event) => {
} }
let email: string | null = emailElement.value; let email: string | null = emailElement.value;
if (!email.replace(/\s/g, '').length) { if (!email.replace(/\s/g, "").length) {
email = null; email = null;
} else { } else {
exists = await emailExists(); exists = await emailExists();
@ -80,11 +80,11 @@ const registerUser = async (e: Event) => {
} }
}; };
export const index = () => { export const index = (): void => {
const form = <HTMLFontElement>document.getElementById('form'); const form = <HTMLFontElement>document.getElementById("form");
form.addEventListener('submit', registerUser, true); form.addEventListener("submit", registerUser, true);
usernameElement.addEventListener( usernameElement.addEventListener(
'input', "input",
async () => await userExists(), async () => await userExists(),
false, false,
); );

View File

@ -14,13 +14,13 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import fetchMock from 'jest-fetch-mock'; import fetchMock from "jest-fetch-mock";
import userExists from './userExists'; import userExists from "./userExists";
import {mockAlert, getLoginFormHtml} from '../../../setUpTests'; import {mockAlert, getLoginFormHtml} from "../../../setUpTests";
import setup from '../../../components/error/setUpTests'; import setup from "../../../components/error/setUpTests";
fetchMock.enableMocks(); fetchMock.enableMocks();
mockAlert(); mockAlert();
@ -29,16 +29,16 @@ beforeEach(() => {
fetchMock.resetMocks(); fetchMock.resetMocks();
}); });
it('finds exchange', async () => { it("finds exchange", async () => {
fetchMock.mockResponseOnce(JSON.stringify({exists: true})); fetchMock.mockResponseOnce(JSON.stringify({exists: true}));
document.body.innerHTML = getLoginFormHtml(); document.body.innerHTML = getLoginFormHtml();
document.querySelector('body').appendChild(setup()); document.querySelector("body").appendChild(setup());
const usernameField = <HTMLInputElement>document.querySelector('#username'); const usernameField = <HTMLInputElement>document.querySelector("#username");
usernameField.value = 'test'; usernameField.value = "test";
expect(await userExists()).toBe(true); expect(await userExists()).toBe(true);
usernameField.value = 'test'; usernameField.value = "test";
fetchMock.mockResponseOnce(JSON.stringify({exists: true})); fetchMock.mockResponseOnce(JSON.stringify({exists: true}));
expect(await userExists(usernameField)).toBe(true); expect(await userExists(usernameField)).toBe(true);

View File

@ -15,16 +15,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import ROUTES from '../../../api/v1/routes'; import ROUTES from "../../../api/v1/routes";
import genJsonPayload from '../../../utils/genJsonPayload'; import genJsonPayload from "../../../utils/genJsonPayload";
import createError from '../../../components/error/index'; import createError from "../../../components/error/index";
const userExists = async (element?: HTMLInputElement) => { const userExists = async (element?: HTMLInputElement): Promise<boolean> => {
console.log(element); console.log(element);
let username; let username;
if (element === undefined) { if (element === undefined) {
username = <HTMLInputElement>document.getElementById('username'); username = <HTMLInputElement>document.getElementById("username");
} else { } else {
username = element; username = element;
} }
@ -37,7 +37,7 @@ const userExists = async (element?: HTMLInputElement) => {
if (res.ok) { if (res.ok) {
const data = await res.json(); const data = await res.json();
if (data.exists) { if (data.exists) {
username.className += ' form__in-field--warn'; username.className += " form__in-field--warn";
createError(`Username "${val}" taken`); createError(`Username "${val}" taken`);
} }
return data.exists; return data.exists;

View File

@ -15,17 +15,17 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import form from './index'; import form from "./index";
it('sudo form works', () => { it("sudo form works", () => {
try { try {
form.get(); form.get();
} catch (e) { } catch (e) {
expect(e.message).toBe('Element form is undefined'); expect(e.message).toBe("Element form is undefined");
} }
const element = document.createElement('form'); const element = document.createElement("form");
element.id = 'form'; element.id = "form";
document.body.appendChild(element); document.body.appendChild(element);
expect(form.get()).toBe(element); expect(form.get()).toBe(element);
}); });

View File

@ -14,9 +14,9 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import LazyElement from '../../utils/lazyElement'; import LazyElement from "../../utils/lazyElement";
const ID = 'form'; const ID = "form";
const FORM = new LazyElement(ID); const FORM = new LazyElement(ID);
export default FORM; export default FORM;

View File

@ -15,9 +15,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import additionalData from './index'; import additionalData from "./index";
it('sudo form works', () => { it("sudo form works", () => {
try { try {
additionalData(); additionalData();
} catch (e) { } catch (e) {
@ -26,8 +26,8 @@ it('sudo form works', () => {
); );
} }
const element = document.createElement('div'); const element = document.createElement("div");
element.id = 'additional-data'; element.id = "additional-data";
document.body.appendChild(element); document.body.appendChild(element);
expect(additionalData()).toBe(element); expect(additionalData()).toBe(element);
}); });

View File

@ -15,9 +15,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
const additionalData = () => { const additionalData = (): HTMLElement => {
let element = null; let element = null;
const ID = 'additional-data'; const ID = "additional-data";
if (element === null) { if (element === null) {
element = <HTMLElement>document.getElementById(ID); element = <HTMLElement>document.getElementById(ID);
@ -29,7 +29,7 @@ const additionalData = () => {
return element; return element;
} }
} else { } else {
element; return element;
} }
}; };

View File

@ -23,7 +23,7 @@ class CopyIcon {
constructor( constructor(
writeText: string, writeText: string,
copyIcon: HTMLElement, copyIcon: HTMLElement,
copyDoneIconClass: string, copyDoneIconClass: string
) { ) {
this.copyIcon = copyIcon; this.copyIcon = copyIcon;
this.copyDoneIconClass = copyDoneIconClass; this.copyDoneIconClass = copyDoneIconClass;
@ -32,24 +32,24 @@ class CopyIcon {
this.__registerHandlers(); this.__registerHandlers();
} }
__registerHandlers() { __registerHandlers(): void {
this.copyIcon.addEventListener('click', e => this.copySitekey(e)); this.copyIcon.addEventListener("click", (e) => this.copySitekey(e));
} }
/* /*
* Copy secret to clipboard * Copy secret to clipboard
*/ */
async copySitekey(e: Event) { async copySitekey(e: Event): Promise<void> {
const image = <HTMLElement>e.target; const image = <HTMLElement>e.target;
const copyDoneIcon = <HTMLElement>( const copyDoneIcon = <HTMLElement>(
image.parentElement.querySelector(`.${this.copyDoneIconClass}`) image.parentElement.querySelector(`.${this.copyDoneIconClass}`)
); );
await navigator.clipboard.writeText(this.writeText); await navigator.clipboard.writeText(this.writeText);
image.style.display = 'none'; image.style.display = "none";
copyDoneIcon.style.display = 'block'; copyDoneIcon.style.display = "block";
setTimeout(() => { setTimeout(() => {
copyDoneIcon.style.display = 'none'; copyDoneIcon.style.display = "none";
image.style.display = 'block'; image.style.display = "block";
}, 1200); }, 1200);
} }
} }

View File

@ -15,16 +15,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import createError from './index'; import createError from "./index";
import * as e from './index'; import * as e from "./index";
import setup from './setUpTests'; import setup from "./setUpTests";
'use strict'; "use strict";
jest.useFakeTimers(); jest.useFakeTimers();
it('checks if error boxes work', () => { it("checks if error boxes work", () => {
document.body.append(setup()); document.body.append(setup());
const getMsg = (num: number) => `message ${num}`; const getMsg = (num: number) => `message ${num}`;
@ -32,21 +32,21 @@ it('checks if error boxes work', () => {
let msg = document.querySelector(`.${e.ERR_MSG_CONTAINER}`); let msg = document.querySelector(`.${e.ERR_MSG_CONTAINER}`);
expect(msg.innerHTML).toContain(getMsg(1)); expect(msg.innerHTML).toContain(getMsg(1));
let btn = <HTMLButtonElement>msg.getElementsByClassName(e.ERR_CLOSE)[0]; const btn = <HTMLButtonElement>msg.getElementsByClassName(e.ERR_CLOSE)[0];
btn.click(); btn.click();
msg = document.querySelector(`.${e.ERR_MSG_CONTAINER}`); msg = document.querySelector(`.${e.ERR_MSG_CONTAINER}`);
expect(msg).toEqual(null); expect(msg).toEqual(null);
const errElement = document.createElement('p'); const errElement = document.createElement("p");
errElement.appendChild(document.createTextNode(getMsg(2))); errElement.appendChild(document.createTextNode(getMsg(2)));
createError(errElement); createError(errElement);
msg = document.querySelector(`.${e.ERR_MSG_CONTAINER}`).querySelector('p'); msg = document.querySelector(`.${e.ERR_MSG_CONTAINER}`).querySelector("p");
expect(msg).toEqual(errElement); expect(msg).toEqual(errElement);
let timeOutElement = document.createElement('p'); const timeOutElement = document.createElement("p");
timeOutElement.appendChild(document.createTextNode(getMsg(2))); timeOutElement.appendChild(document.createTextNode(getMsg(2)));
createError(timeOutElement, 200); createError(timeOutElement, 200);
msg = document.querySelector(`.${e.ERR_MSG_CONTAINER}`).querySelector('p'); msg = document.querySelector(`.${e.ERR_MSG_CONTAINER}`).querySelector("p");
expect(msg).toEqual(timeOutElement); expect(msg).toEqual(timeOutElement);
jest.runOnlyPendingTimers(); jest.runOnlyPendingTimers();
msg = document.querySelector(`.${e.ERR_MSG_CONTAINER}`); msg = document.querySelector(`.${e.ERR_MSG_CONTAINER}`);

View File

@ -15,9 +15,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
export const ERR_CONTAINER_ID = 'err__container'; export const ERR_CONTAINER_ID = "err__container";
export const ERR_MSG_CONTAINER = 'err__msg-container'; // class export const ERR_MSG_CONTAINER = "err__msg-container"; // class
export const ERR_CLOSE = 'err__close'; // class export const ERR_CLOSE = "err__close"; // class
export const DEFAULT_LIFETIME = 5000; export const DEFAULT_LIFETIME = 5000;
@ -41,11 +41,11 @@ const err = () => {
const createError = ( const createError = (
message: string | HTMLElement, message: string | HTMLElement,
lifetime: number = DEFAULT_LIFETIME, lifetime: number = DEFAULT_LIFETIME,
) => { ): void => {
const box = document.createElement('div'); const box = document.createElement("div");
const msg = () => { const msg = () => {
if (typeof message === 'string') { if (typeof message === "string") {
return document.createTextNode(message); return document.createTextNode(message);
} else { } else {
return message; return message;
@ -55,8 +55,8 @@ const createError = (
box.className = ERR_MSG_CONTAINER; box.className = ERR_MSG_CONTAINER;
box.appendChild(msg()); box.appendChild(msg());
const deleteBtn = document.createElement('button'); const deleteBtn = document.createElement("button");
const deleteMsg = document.createTextNode('x'); const deleteMsg = document.createTextNode("x");
deleteBtn.appendChild(deleteMsg); deleteBtn.appendChild(deleteMsg);
deleteBtn.className = ERR_CLOSE; deleteBtn.className = ERR_CLOSE;
box.appendChild(deleteBtn); box.appendChild(deleteBtn);
@ -71,7 +71,7 @@ const createError = (
box.remove(); box.remove();
}; };
deleteBtn.addEventListener('click', e => deleteHandler(e)); deleteBtn.addEventListener("click", e => deleteHandler(e));
}; };
export default createError; export default createError;

View File

@ -14,10 +14,10 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import * as e from './index'; import * as e from "./index";
const setup = () => { const setup = (): HTMLElement => {
let x = document.createElement('div'); const x = document.createElement("div");
x.id = e.ERR_CONTAINER_ID; x.id = e.ERR_CONTAINER_ID;
return x; return x;
}; };

View File

@ -15,12 +15,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
const showPasswordButtonClassHidden = 'show-password--hide'; const showPasswordButtonClassHidden = "show-password--hide";
const showPasswordButtonClassShowing = 'show-password--show'; const showPasswordButtonClassShowing = "show-password--show";
const container = 'show-password-container'; const container = "show-password-container";
let display = 'hidden'; let display = "hidden";
const showPasswordButtons = () => { const showPasswordButtons = () => {
let buttons: NodeListOf<HTMLElement>; let buttons: NodeListOf<HTMLElement>;
@ -49,45 +49,45 @@ const hidePasswordButtons = () => {
}; };
// e is click event from show password container // e is click event from show password container
export const showPassword = () => { export const showPassword = (): void => {
const inputs = document.body.querySelectorAll('input'); const inputs = document.body.querySelectorAll("input");
if (display == 'hidden') { if (display == "hidden") {
display = 'show'; display = "show";
inputs.forEach(element => { inputs.forEach(element => {
if (element.type === 'password') { if (element.type === "password") {
element.type = 'text'; element.type = "text";
} }
}); });
showPasswordButtons().forEach((button: HTMLInputElement) => { showPasswordButtons().forEach((button: HTMLInputElement) => {
button.style.display = 'none'; button.style.display = "none";
}); });
hidePasswordButtons().forEach((button: HTMLInputElement) => { hidePasswordButtons().forEach((button: HTMLInputElement) => {
button.style.display = 'inline'; button.style.display = "inline";
}); });
} else { } else {
display = 'hidden'; display = "hidden";
inputs.forEach(element => { inputs.forEach(element => {
if (element.type === 'text' && element.name.includes('password')) { if (element.type === "text" && element.name.includes("password")) {
element.type = 'password'; element.type = "password";
} }
}); });
showPasswordButtons().forEach((button: HTMLInputElement) => { showPasswordButtons().forEach((button: HTMLInputElement) => {
button.style.display = 'inline'; button.style.display = "inline";
}); });
hidePasswordButtons().forEach((button: HTMLInputElement) => { hidePasswordButtons().forEach((button: HTMLInputElement) => {
button.style.display = 'none'; button.style.display = "none";
}); });
} }
// posibily clicked on something else // posibily clicked on something else
}; };
export const registerShowPassword = () => { export const registerShowPassword = (): void => {
document.querySelectorAll(`.${container}`).forEach(container => { document.querySelectorAll(`.${container}`).forEach(container => {
container.addEventListener('click', showPassword); container.addEventListener("click", showPassword);
}); });
}; };

View File

@ -15,8 +15,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import registerShowPassword from './index'; import registerShowPassword from "./index";
import {showPassword} from './index'; import {showPassword} from "./index";
const initial_content = ` const initial_content = `
<form class="sitekey-form" method="POST" action="/api/v1/signin" id="form" data-bitwarden-watching="1"> <form class="sitekey-form" method="POST" action="/api/v1/signin" id="form" data-bitwarden-watching="1">
@ -41,49 +41,49 @@ const initial_content = `
</form> </form>
`; `;
it('show password works', () => { it("show password works", () => {
document.body.innerHTML = initial_content; document.body.innerHTML = initial_content;
const container = <HTMLElement>( const container = <HTMLElement>(
document.querySelector(`.show-password-container`) document.querySelector(".show-password-container")
); );
const hide = <HTMLElement>container.querySelector('.show-password--hide'); const hide = <HTMLElement>container.querySelector(".show-password--hide");
const show = <HTMLElement>container.querySelector('.show-password--show'); const show = <HTMLElement>container.querySelector(".show-password--show");
const password = <HTMLInputElement>document.getElementById('password'); const password = <HTMLInputElement>document.getElementById("password");
show.style.display = 'inline'; show.style.display = "inline";
hide.style.display = 'none'; hide.style.display = "none";
showPassword(); showPassword();
expect(hide.style.display).toEqual('inline'); expect(hide.style.display).toEqual("inline");
expect(show.style.display).toEqual('none'); expect(show.style.display).toEqual("none");
expect(password.type).toEqual('text'); expect(password.type).toEqual("text");
showPassword(); showPassword();
expect(show.style.display).toEqual('inline'); expect(show.style.display).toEqual("inline");
expect(hide.style.display).toEqual('none'); expect(hide.style.display).toEqual("none");
expect(password.type).toEqual('password'); expect(password.type).toEqual("password");
}); });
it('show password click works', () => { it("show password click works", () => {
document.body.innerHTML = initial_content; document.body.innerHTML = initial_content;
const container = <HTMLElement>( const container = <HTMLElement>(
document.querySelector(`.show-password-container`) document.querySelector(".show-password-container")
); );
const hide = <HTMLElement>container.querySelector('.show-password--hide'); const hide = <HTMLElement>container.querySelector(".show-password--hide");
const show = <HTMLElement>container.querySelector('.show-password--show'); const show = <HTMLElement>container.querySelector(".show-password--show");
const password = <HTMLInputElement>document.getElementById('password'); const password = <HTMLInputElement>document.getElementById("password");
show.style.display = 'inline'; show.style.display = "inline";
hide.style.display = 'none'; hide.style.display = "none";
registerShowPassword(); registerShowPassword();
container.click(); container.click();
expect(hide.style.display).toEqual('inline'); expect(hide.style.display).toEqual("inline");
expect(show.style.display).toEqual('none'); expect(show.style.display).toEqual("none");
expect(password.type).toEqual('text'); expect(password.type).toEqual("text");
container.click(); container.click();
expect(show.style.display).toEqual('inline'); expect(show.style.display).toEqual("inline");
expect(hide.style.display).toEqual('none'); expect(hide.style.display).toEqual("none");
expect(password.type).toEqual('password'); expect(password.type).toEqual("password");
}); });

View File

@ -15,39 +15,39 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {Router} from './router'; import {Router} from "./router";
import * as login from './auth/login/ts/'; import * as login from "./auth/login/ts/";
import * as register from './auth/register/ts/'; import * as register from "./auth/register/ts/";
import * as panel from './panel/ts/index'; import * as panel from "./panel/ts/index";
import settings from './panel/settings/'; import settings from "./panel/settings/";
import * as deleteAccount from './panel/settings/account/delete'; import * as deleteAccount from "./panel/settings/account/delete";
import * as updateSecret from './panel/settings/secret/update'; import * as updateSecret from "./panel/settings/secret/update";
import * as addSiteKey from './panel/sitekey/add/ts'; import * as addSiteKey from "./panel/sitekey/add/ts";
import * as editSitekey from './panel/sitekey/edit/'; import * as editSitekey from "./panel/sitekey/edit/";
import * as deleteSitekey from './panel/sitekey/delete/'; import * as deleteSitekey from "./panel/sitekey/delete/";
import * as listSitekeys from './panel/sitekey/list/ts'; import * as listSitekeys from "./panel/sitekey/list/ts";
import * as notidications from './panel/notifications/ts'; import * as notidications from "./panel/notifications/ts";
import {MODE} from './logger'; import {MODE} from "./logger";
import log from './logger'; import log from "./logger";
import VIEWS from './views/v1/routes'; import VIEWS from "./views/v1/routes";
import './main.scss'; import "./main.scss";
import './auth/css/main.scss'; import "./auth/css/main.scss";
import './components/details-footer/main.scss'; import "./components/details-footer/main.scss";
import './components/error/main.scss'; import "./components/error/main.scss";
import './components/showPassword/main.scss'; import "./components/showPassword/main.scss";
import './panel/css/main.scss'; import "./panel/css/main.scss";
import './panel/navbar/main.scss'; import "./panel/navbar/main.scss";
import './panel/settings/main.scss'; import "./panel/settings/main.scss";
import './panel/notifications/main.scss'; import "./panel/notifications/main.scss";
import './panel/header/taskbar/main.scss'; import "./panel/header/taskbar/main.scss";
import './panel/help-banner/main.scss'; import "./panel/help-banner/main.scss";
import './panel/sitekey/add/css/main.scss'; import "./panel/sitekey/add/css/main.scss";
import './panel/sitekey/list/css/main.scss'; import "./panel/sitekey/list/css/main.scss";
import './errors/main.scss'; import "./errors/main.scss";
log.setMode(MODE.production); log.setMode(MODE.production);
@ -62,8 +62,8 @@ router.register(VIEWS.loginUser, login.index);
router.register(VIEWS.notifications, notidications.index); router.register(VIEWS.notifications, notidications.index);
router.register(VIEWS.listSitekey, listSitekeys.index); router.register(VIEWS.listSitekey, listSitekeys.index);
router.register(VIEWS.addSiteKey, addSiteKey.index); router.register(VIEWS.addSiteKey, addSiteKey.index);
router.register(VIEWS.editSitekey('[A-Z),a-z,0-9]+'), editSitekey.index); router.register(VIEWS.editSitekey("[A-Z),a-z,0-9]+"), editSitekey.index);
router.register(VIEWS.deleteSitekey('[A-Z),a-z,0-9]+'), deleteSitekey.index); router.register(VIEWS.deleteSitekey("[A-Z),a-z,0-9]+"), deleteSitekey.index);
try { try {
router.route(); router.route();

View File

@ -15,13 +15,13 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import './mobile.scss'; import "./mobile.scss";
import './auth/css/mobile.scss'; import "./auth/css/mobile.scss";
import './components/details-footer/mobile.scss'; import "./components/details-footer/mobile.scss";
import './panel/css/mobile.scss'; import "./panel/css/mobile.scss";
import './panel/settings/mobile.scss'; import "./panel/settings/mobile.scss";
import './panel/header/taskbar/mobile.scss'; import "./panel/header/taskbar/mobile.scss";
import './panel/navbar/mobile.scss'; import "./panel/navbar/mobile.scss";
import './panel/help-banner/mobile.scss'; import "./panel/help-banner/mobile.scss";
import './panel/sitekey/add/css/mobile.scss'; import "./panel/sitekey/add/css/mobile.scss";
import './panel/sitekey/list/css/mobile.scss'; import "./panel/sitekey/list/css/mobile.scss";

View File

@ -14,4 +14,4 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import './main.scss'; import "./main.scss";

View File

@ -15,13 +15,13 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import genJsonPayload from '../../../utils/genJsonPayload'; import genJsonPayload from "../../../utils/genJsonPayload";
import createError from '../../../components/error'; import createError from "../../../components/error";
import ROUTES from '../../../api/v1/routes'; import ROUTES from "../../../api/v1/routes";
const BTN = document.querySelectorAll('.notification__mark-read-btn'); const BTN = document.querySelectorAll(".notification__mark-read-btn");
const TABLE_BODY = document.querySelector('.notification__body'); const TABLE_BODY = document.querySelector(".notification__body");
const notification_record = (id: number) => const notification_record = (id: number) =>
<HTMLElement>TABLE_BODY.querySelector(`#notification__item-${id}`); <HTMLElement>TABLE_BODY.querySelector(`#notification__item-${id}`);
@ -46,10 +46,10 @@ const markRead = async (e: Event) => {
const addMarkReadEventListenet = () => { const addMarkReadEventListenet = () => {
BTN.forEach(btn => { BTN.forEach(btn => {
btn.addEventListener('click', markRead, true); btn.addEventListener("click", markRead, true);
}); });
}; };
export const index = () => { export const index = (): void => {
addMarkReadEventListenet(); addMarkReadEventListenet();
}; };

View File

@ -15,15 +15,15 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {getPassword} from '../../../auth/login/ts/'; import {getPassword} from "../../../auth/login/ts/";
import FORM from '../../../auth/sudo/'; import FORM from "../../../auth/sudo/";
import getFormUrl from '../../../utils/getFormUrl'; import getFormUrl from "../../../utils/getFormUrl";
import genJsonPayload from '../../../utils/genJsonPayload'; import genJsonPayload from "../../../utils/genJsonPayload";
import createError from '../../../components/error'; import createError from "../../../components/error";
import registerShowPassword from '../../../components/showPassword'; import registerShowPassword from "../../../components/showPassword";
import VIEWS from '../../../views/v1/routes'; import VIEWS from "../../../views/v1/routes";
const submit = async (e: Event) => { const submit = async (e: Event) => {
e.preventDefault(); e.preventDefault();
@ -44,7 +44,7 @@ const submit = async (e: Event) => {
} }
}; };
export const index = () => { export const index = (): void => {
FORM.get().addEventListener('submit', submit, true); FORM.get().addEventListener("submit", submit, true);
registerShowPassword(); registerShowPassword();
}; };

View File

@ -15,28 +15,28 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import registerShowPassword from '../../components/showPassword/'; import registerShowPassword from "../../components/showPassword/";
import CopyIcon from '../../components/clipboard/'; import CopyIcon from "../../components/clipboard/";
import createError from '../../components/error/'; import createError from "../../components/error/";
import emailExists from '../../auth/register/ts/emailExists'; import emailExists from "../../auth/register/ts/emailExists";
import userExists from '../../auth/register/ts/userExists'; import userExists from "../../auth/register/ts/userExists";
import LazyElement from '../../utils/lazyElement'; import LazyElement from "../../utils/lazyElement";
import isBlankString from '../../utils/isBlankString'; import isBlankString from "../../utils/isBlankString";
import getFormUrl from '../../utils/getFormUrl'; import getFormUrl from "../../utils/getFormUrl";
import genJsonPayload from '../../utils/genJsonPayload'; import genJsonPayload from "../../utils/genJsonPayload";
import VIEWS from '../../views/v1/routes'; import VIEWS from "../../views/v1/routes";
const SECRET_COPY_ICON = 'settings__secret-copy'; const SECRET_COPY_ICON = "settings__secret-copy";
const SECRET_COPY_DONE_ICON = 'settings__secret-copy-done'; const SECRET_COPY_DONE_ICON = "settings__secret-copy-done";
// form IDs // form IDs
const DELETE_FORM = 'settings__delete-form'; const DELETE_FORM = "settings__delete-form";
const EMAIL_FORM = 'settings__email-form'; const EMAIL_FORM = "settings__email-form";
const USERNAME_FORM = 'settings__username-form'; const USERNAME_FORM = "settings__username-form";
const SECRET_FORM = 'settings__secret-form'; const SECRET_FORM = "settings__secret-form";
// form elements // form elements
const deleteForm = new LazyElement(DELETE_FORM); const deleteForm = new LazyElement(DELETE_FORM);
@ -45,8 +45,8 @@ const usernameForm = new LazyElement(USERNAME_FORM);
const secretForm = new LazyElement(SECRET_FORM); const secretForm = new LazyElement(SECRET_FORM);
// field IDs // field IDs
const EMAIL = 'email'; const EMAIL = "email";
const USERNAME = 'username'; const USERNAME = "username";
// field elements // field elements
const emailField = new LazyElement(EMAIL); const emailField = new LazyElement(EMAIL);
@ -57,7 +57,7 @@ const updateEmail = async (e: Event) => {
e.preventDefault(); e.preventDefault();
const emailElement = <HTMLInputElement>emailField.get(); const emailElement = <HTMLInputElement>emailField.get();
const email = emailElement.value; const email = emailElement.value;
isBlankString(email, 'email', e); isBlankString(email, "email", e);
if (await emailExists(emailElement)) { if (await emailExists(emailElement)) {
return; return;
} else { } else {
@ -80,7 +80,7 @@ const updateUsername = async (e: Event) => {
e.preventDefault(); e.preventDefault();
const usernameElement = <HTMLInputElement>usernameField.get(); const usernameElement = <HTMLInputElement>usernameField.get();
const username = usernameElement.value; const username = usernameElement.value;
isBlankString(username, 'username', e); isBlankString(username, "username", e);
if (await userExists(usernameElement)) { if (await userExists(usernameElement)) {
return; return;
} else { } else {
@ -101,7 +101,7 @@ const updateUsername = async (e: Event) => {
const updateSecret = (e: Event) => { const updateSecret = (e: Event) => {
e.preventDefault(); e.preventDefault();
const msg = const msg =
'WARNING: updating secret will cause service disruption if old secret is still in use post update'; "WARNING: updating secret will cause service disruption if old secret is still in use post update";
if (confirm(msg)) { if (confirm(msg)) {
window.location.assign(VIEWS.updateSecret); window.location.assign(VIEWS.updateSecret);
} }
@ -118,14 +118,14 @@ const deleteAccount = (e: Event) => {
// regist form event handlers // regist form event handlers
const registerForms = () => { const registerForms = () => {
deleteForm.get().addEventListener('submit', e => deleteAccount(e), true); deleteForm.get().addEventListener("submit", (e) => deleteAccount(e), true);
emailForm.get().addEventListener('submit', e => updateEmail(e), true); emailForm.get().addEventListener("submit", (e) => updateEmail(e), true);
usernameForm.get().addEventListener('submit', e => updateUsername(e), true); usernameForm.get().addEventListener("submit", (e) => updateUsername(e), true);
console.log(usernameField.get()); console.log(usernameField.get());
usernameField usernameField
.get() .get()
.addEventListener('input', async () => await userExists(), false); .addEventListener("input", async () => await userExists(), false);
secretForm.get().addEventListener('submit', e => updateSecret(e), true); secretForm.get().addEventListener("submit", (e) => updateSecret(e), true);
}; };
// set up copying account secret to clipboard // set up copying account secret to clipboard
@ -138,7 +138,7 @@ const initCopySecret = () => {
}; };
/// TODO email update button should only change if email value has been changed /// TODO email update button should only change if email value has been changed
const index = () => { const index = (): void => {
registerShowPassword(); registerShowPassword();
initCopySecret(); initCopySecret();
registerForms(); registerForms();

View File

@ -15,15 +15,15 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {getPassword} from '../../../auth/login/ts/'; import {getPassword} from "../../../auth/login/ts/";
import FORM from '../../../auth/sudo/'; import FORM from "../../../auth/sudo/";
import getFormUrl from '../../../utils/getFormUrl'; import getFormUrl from "../../../utils/getFormUrl";
import genJsonPayload from '../../../utils/genJsonPayload'; import genJsonPayload from "../../../utils/genJsonPayload";
import createError from '../../../components/error'; import createError from "../../../components/error";
import registerShowPassword from '../../../components/showPassword'; import registerShowPassword from "../../../components/showPassword";
import VIEWS from '../../../views/v1/routes'; import VIEWS from "../../../views/v1/routes";
const submit = async (e: Event) => { const submit = async (e: Event) => {
e.preventDefault(); e.preventDefault();
@ -44,7 +44,7 @@ const submit = async (e: Event) => {
} }
}; };
export const index = () => { export const index = (): void => {
FORM.get().addEventListener('submit', submit, true); FORM.get().addEventListener("submit", submit, true);
registerShowPassword(); registerShowPassword();
}; };

View File

@ -15,16 +15,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import getNumLevels from './levels/getNumLevels'; import getNumLevels from "./levels/getNumLevels";
import {getAddForm, trim, addLevel} from './setupTests'; import {getAddForm, trim, addLevel} from "./setupTests";
import setup from '../../../../components/error/setUpTests'; import setup from "../../../../components/error/setUpTests";
document.body.innerHTML = getAddForm(); document.body.innerHTML = getAddForm();
document.body.appendChild(setup()); document.body.appendChild(setup());
jest.useFakeTimers(); jest.useFakeTimers();
it('addLevelButton works', () => { it("addLevelButton works", () => {
expect(getNumLevels()).toBe(1); expect(getNumLevels()).toBe(1);
// add a level // add a level
addLevel(2, 4); addLevel(2, 4);
@ -36,7 +36,7 @@ it('addLevelButton works', () => {
addLevel(4, 9); addLevel(4, 9);
expect(getNumLevels()).toBe(3); expect(getNumLevels()).toBe(3);
let a = document.body.innerHTML; const a = document.body.innerHTML;
expect(trim(a)).toBe(trim(finalHtml())); expect(trim(a)).toBe(trim(finalHtml()));

View File

@ -14,16 +14,16 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import validateLevel from './levels/validateLevel'; import validateLevel from "./levels/validateLevel";
import getNumLevels from './levels/getNumLevels'; import getNumLevels from "./levels/getNumLevels";
import * as UpdateLevel from './levels/updateLevel'; import * as UpdateLevel from "./levels/updateLevel";
import { import {
getRemoveButtonHTML, getRemoveButtonHTML,
addRemoveLevelButtonEventListener, addRemoveLevelButtonEventListener,
} from './removeLevelButton'; } from "./removeLevelButton";
import CONST from './const'; import CONST from "./const";
import log from '../../../../logger'; import log from "../../../../logger";
/** /**
* Gets executed when 'Add' Button is clicked to add levels * Gets executed when 'Add' Button is clicked to add levels
@ -39,30 +39,30 @@ const addLevel = (e: Event) => {
const isValid = validateLevel(onScreenLevel); const isValid = validateLevel(onScreenLevel);
log.debug(`[addLevelButton] isValid: ${isValid}`); log.debug(`[addLevelButton] isValid: ${isValid}`);
if (!isValid) { if (!isValid) {
let error = `Aborting level ${onScreenLevel} addition`; const error = `Aborting level ${onScreenLevel} addition`;
return log.error(error); return log.error(error);
} }
FIELDSET.replaceChild(getRemoveButtonHTML(onScreenLevel), PARENT); FIELDSET.replaceChild(getRemoveButtonHTML(onScreenLevel), PARENT);
const newLevelElement = getHtml(onScreenLevel + 1); const newLevelElement = getHtml(onScreenLevel + 1);
FIELDSET.insertAdjacentElement('afterend', newLevelElement); FIELDSET.insertAdjacentElement("afterend", newLevelElement);
UpdateLevel.register(onScreenLevel); UpdateLevel.register(onScreenLevel);
addRemoveLevelButtonEventListener(onScreenLevel); addRemoveLevelButtonEventListener(onScreenLevel);
addLevelButtonAddEventListener(); addLevelButtonAddEventListener();
const main = document.querySelector('body'); const main = document.querySelector("body");
const style = main.style.display; const style = main.style.display;
main.style.display = 'none'; main.style.display = "none";
main.style.display = style; main.style.display = style;
}; };
/** adds onclick event listener */ /** adds onclick event listener */
const addLevelButtonAddEventListener = () => { const addLevelButtonAddEventListener = (): void => {
const addLevelButton = <HTMLElement>( const addLevelButton = <HTMLElement>(
document.querySelector(`.${CONST.ADD_LEVEL_BUTTON}`) document.querySelector(`.${CONST.ADD_LEVEL_BUTTON}`)
); );
addLevelButton.addEventListener('click', addLevel); addLevelButton.addEventListener("click", addLevel);
}; };
/** /**
@ -72,25 +72,25 @@ const addLevelButtonAddEventListener = () => {
const getHtml = (level: number) => { const getHtml = (level: number) => {
log.debug(`[generating HTML getHtml]level: ${level}`); log.debug(`[generating HTML getHtml]level: ${level}`);
const fieldset = document.createElement('fieldset'); // new HTMLFieldSetElement(); const fieldset = document.createElement("fieldset"); // new HTMLFieldSetElement();
fieldset.className = CONST.LEVEL_CONTAINER_CLASS; fieldset.className = CONST.LEVEL_CONTAINER_CLASS;
fieldset.id = `${CONST.LEVEL_FIELDSET_ID_WITHOUT_LEVEL}${level}`; fieldset.id = `${CONST.LEVEL_FIELDSET_ID_WITHOUT_LEVEL}${level}`;
const legend = document.createElement('legend'); // new HTMLLegendElement(); const legend = document.createElement("legend"); // new HTMLLegendElement();
legend.className = CONST.LEGEND_CLASS; legend.className = CONST.LEGEND_CLASS;
const legendText = document.createTextNode(`Level ${level}`); const legendText = document.createTextNode(`Level ${level}`);
legend.appendChild(legendText); legend.appendChild(legendText);
fieldset.appendChild(legend); fieldset.appendChild(legend);
const vistitorLabel = document.createElement('label'); //document.createElement('label'); const vistitorLabel = document.createElement("label"); //document.createElement('label');
vistitorLabel.className = CONST.LABEL_CLASS; vistitorLabel.className = CONST.LABEL_CLASS;
const visitorText = document.createTextNode('Visitor'); const visitorText = document.createTextNode("Visitor");
vistitorLabel.appendChild(visitorText); vistitorLabel.appendChild(visitorText);
const visitor = document.createElement('input'); //document.createElement('input'); const visitor = document.createElement("input"); //document.createElement('input');
const visitorId = `${CONST.VISITOR_WITHOUT_LEVEL}${level}`; const visitorId = `${CONST.VISITOR_WITHOUT_LEVEL}${level}`;
visitor.className = CONST.LEVEL_INPUT_CLASS; visitor.className = CONST.LEVEL_INPUT_CLASS;
visitor.type = 'number'; visitor.type = "number";
visitor.name = visitorId; visitor.name = visitorId;
visitor.id = visitorId; visitor.id = visitorId;
vistitorLabel.htmlFor = visitorId; vistitorLabel.htmlFor = visitorId;
@ -98,13 +98,13 @@ const getHtml = (level: number) => {
fieldset.appendChild(vistitorLabel); fieldset.appendChild(vistitorLabel);
const difficultyLabel = document.createElement('label'); const difficultyLabel = document.createElement("label");
difficultyLabel.className = CONST.LABEL_CLASS; difficultyLabel.className = CONST.LABEL_CLASS;
const difficultyText = document.createTextNode('Difficulty'); const difficultyText = document.createTextNode("Difficulty");
difficultyLabel.appendChild(difficultyText); difficultyLabel.appendChild(difficultyText);
const difficulty = document.createElement('input'); const difficulty = document.createElement("input");
const difficultyID = `${CONST.DIFFICULTY_WITHOUT_LEVEL}${level}`; const difficultyID = `${CONST.DIFFICULTY_WITHOUT_LEVEL}${level}`;
difficulty.type = 'number'; difficulty.type = "number";
difficulty.name = difficultyID; difficulty.name = difficultyID;
difficulty.className = CONST.LEVEL_INPUT_CLASS; difficulty.className = CONST.LEVEL_INPUT_CLASS;
difficulty.id = difficultyID; difficulty.id = difficultyID;
@ -113,18 +113,18 @@ const getHtml = (level: number) => {
fieldset.appendChild(difficultyLabel); fieldset.appendChild(difficultyLabel);
const addLevelLabel = document.createElement('label'); const addLevelLabel = document.createElement("label");
addLevelLabel.className = CONST.REMOVE_LEVEL_LABEL_CLASS; addLevelLabel.className = CONST.REMOVE_LEVEL_LABEL_CLASS;
const addLevel = document.createElement('input'); const addLevel = document.createElement("input");
addLevel.className = CONST.ADD_LEVEL_BUTTON; addLevel.className = CONST.ADD_LEVEL_BUTTON;
addLevel.type = 'button'; addLevel.type = "button";
const addLevelButtonID = 'add'; const addLevelButtonID = "add";
addLevel.name = addLevelButtonID; addLevel.name = addLevelButtonID;
addLevel.id = addLevelButtonID; addLevel.id = addLevelButtonID;
addLevelLabel.htmlFor = addLevelButtonID; addLevelLabel.htmlFor = addLevelButtonID;
const addText = document.createTextNode('Add level'); const addText = document.createTextNode("Add level");
addLevelLabel.appendChild(addText); addLevelLabel.appendChild(addText);
addLevel.value = 'Add'; addLevel.value = "Add";
addLevelLabel.appendChild(addLevel); addLevelLabel.appendChild(addLevel);
fieldset.appendChild(addLevelLabel); fieldset.appendChild(addLevelLabel);

View File

@ -15,25 +15,25 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
const LABEL_INNER_TEXT_WITHOUT_LEVEL = 'Level '; const LABEL_INNER_TEXT_WITHOUT_LEVEL = "Level ";
const LABEL_CLASS = 'sitekey-form__level-label'; const LABEL_CLASS = "sitekey-form__level-label";
const INPUT_ID_WITHOUT_LEVEL = 'level'; const INPUT_ID_WITHOUT_LEVEL = "level";
const LEVEL_INPUT_CLASS = 'sitekey-form__level-input'; const LEVEL_INPUT_CLASS = "sitekey-form__level-input";
const VISITOR_WITHOUT_LEVEL = 'visitor'; const VISITOR_WITHOUT_LEVEL = "visitor";
const DIFFICULTY_WITHOUT_LEVEL = 'difficulty'; const DIFFICULTY_WITHOUT_LEVEL = "difficulty";
const LEVEL_CONTAINER_CLASS = 'sitekey__level-container'; const LEVEL_CONTAINER_CLASS = "sitekey__level-container";
const LEVEL_FIELDSET_ID_WITHOUT_LEVEL = 'level-group-'; const LEVEL_FIELDSET_ID_WITHOUT_LEVEL = "level-group-";
const LEGEND_CLASS = 'sitekey__level-title'; const LEGEND_CLASS = "sitekey__level-title";
const REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL = 'remove-level'; const REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL = "remove-level";
const REMOVE_LEVEL_BUTTON_CLASS = 'sitekey-form__level-remove-level-button'; const REMOVE_LEVEL_BUTTON_CLASS = "sitekey-form__level-remove-level-button";
const REMOVE_LEVEL_LABEL_TEXT = 'Remove Level'; const REMOVE_LEVEL_LABEL_TEXT = "Remove Level";
const REMOVE_LEVEL_LABEL_CLASS = 'sitekey-form__level-label--hidden'; const REMOVE_LEVEL_LABEL_CLASS = "sitekey-form__level-label--hidden";
const ADD_LEVEL_BUTTON = 'sitekey-form__level-add-level-button'; const ADD_LEVEL_BUTTON = "sitekey-form__level-add-level-button";
const CONST = { const CONST = {
LABEL_CLASS, LABEL_CLASS,

View File

@ -15,30 +15,31 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {LEVELS} from '../levels'; import { LEVELS } from "../levels";
import getFormUrl from '../../../../../utils/getFormUrl'; import getFormUrl from "../../../../../utils/getFormUrl";
import genJsonPayload from '../../../../../utils/genJsonPayload'; import genJsonPayload from "../../../../../utils/genJsonPayload";
import VIEWS from '../../../../../views/v1/routes'; import VIEWS from "../../../../../views/v1/routes";
import validateDescription from './validateDescription'; import validateDescription from "./validateDescription";
import validateDuration from './validateDuration'; import validateDuration from "./validateDuration";
import createError from '../../../../../components/error'; import createError from "../../../../../components/error";
export const SITE_KEY_FORM_CLASS = 'sitekey-form'; export const SITE_KEY_FORM_CLASS = "sitekey-form";
export const FORM = <HTMLFormElement>document.querySelector(`.${SITE_KEY_FORM_CLASS}`); export const FORM = <HTMLFormElement>(
document.querySelector(`.${SITE_KEY_FORM_CLASS}`)
);
export const addSubmitEventListener = () => { export const addSubmitEventListener = (): void =>
FORM.addEventListener('submit', submit, true); FORM.addEventListener("submit", submit, true);
};
const submit = async (e: Event) => { const submit = async (e: Event) => {
e.preventDefault(); e.preventDefault();
const description = validateDescription(e); const description = validateDescription(e);
const duration = validateDuration(e); const duration = validateDuration();
const formUrl = getFormUrl(FORM); const formUrl = getFormUrl(FORM);

View File

@ -15,11 +15,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import validateDescription from './validateDescription'; import validateDescription from "./validateDescription";
import {getAddForm, fillDescription} from '../setupTests'; import {getAddForm, fillDescription} from "../setupTests";
import {mockAlert} from '../../../../../setUpTests'; import {mockAlert} from "../../../../../setUpTests";
import setup from '../../../../../components/error/setUpTests'; import setup from "../../../../../components/error/setUpTests";
mockAlert(); mockAlert();
@ -27,17 +27,17 @@ document.body.innerHTML = getAddForm();
const emptyErr = "can't be empty"; const emptyErr = "can't be empty";
it('validateDescription workds', () => { it("validateDescription workds", () => {
document.querySelector('body').appendChild(setup()); document.querySelector("body").appendChild(setup());
try { try {
const event = new Event('submit'); const event = new Event("submit");
validateDescription(event); validateDescription(event);
} catch (e) { } catch (e) {
expect(e.message).toContain(emptyErr); expect(e.message).toContain(emptyErr);
} }
// fill and validate // fill and validate
fillDescription('testing'); fillDescription("testing");
const event = new Event('submit'); const event = new Event("submit");
validateDescription(event); validateDescription(event);
}); });

View File

@ -15,12 +15,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import isBlankString from '../../../../../utils/isBlankString'; import isBlankString from "../../../../../utils/isBlankString";
const validateDescription = (e: Event) => { const validateDescription = (e: Event): string => {
const inputElement = <HTMLInputElement>document.getElementById('description'); const inputElement = <HTMLInputElement>document.getElementById("description");
const val = inputElement.value; const val = inputElement.value;
const filed = 'Description'; const filed = "Description";
isBlankString(val, filed, e); isBlankString(val, filed, e);
return val; return val;
}; };

View File

@ -14,8 +14,6 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import isNumber from '../../../../../utils/isNumber';
//const validateDuration = (e: Event) => { //const validateDuration = (e: Event) => {
// const duartionElement = <HTMLInputElement>document.getElementById('duration'); // const duartionElement = <HTMLInputElement>document.getElementById('duration');
// const duration = parseInt(duartionElement.value); // const duration = parseInt(duartionElement.value);
@ -31,30 +29,28 @@ import isNumber from '../../../../../utils/isNumber';
// //
//export default validateDuration; //export default validateDuration;
import validateDuration from './validateDuration'; import validateDuration from "./validateDuration";
import {getAddForm, fillDuration} from '../setupTests'; import {getAddForm, fillDuration} from "../setupTests";
document.body.innerHTML = getAddForm(); document.body.innerHTML = getAddForm();
const emptyErr = "can't be empty"; const emptyErr = "can't be empty";
const NaNErr = 'duration can contain nubers only'; const NaNErr = "duration can contain nubers only";
const zeroErr = 'duration must be greater than zero'; const zeroErr = "duration must be greater than zero";
const duration = 30; const duration = 30;
it('validateDuration workds', () => { it("validateDuration workds", () => {
try { try {
const event = new Event('submit'); validateDuration();
validateDuration(event);
} catch (e) { } catch (e) {
expect(e.message).toContain(emptyErr); expect(e.message).toContain(emptyErr);
} }
// fill string error // fill string error
try { try {
fillDuration('testing'); fillDuration("testing");
const event = new Event('submit'); validateDuration();
validateDuration(event);
} catch (e) { } catch (e) {
expect(e.message).toContain(NaNErr); expect(e.message).toContain(NaNErr);
} }
@ -62,13 +58,11 @@ it('validateDuration workds', () => {
// zero err // zero err
try { try {
fillDuration(0); fillDuration(0);
const event = new Event('submit'); validateDuration();
validateDuration(event);
} catch (e) { } catch (e) {
expect(e.message).toContain(zeroErr); expect(e.message).toContain(zeroErr);
} }
fillDuration(duration); fillDuration(duration);
const event = new Event('submit'); expect(validateDuration()).toBe(duration);
expect(validateDuration(event)).toBe(duration);
}); });

View File

@ -14,17 +14,17 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import isNumber from '../../../../../utils/isNumber'; import isNumber from "../../../../../utils/isNumber";
const validateDuration = (e: Event) => { const validateDuration = (): number => {
const duartionElement = <HTMLInputElement>document.getElementById('duration'); const duartionElement = <HTMLInputElement>document.getElementById("duration");
const duration = parseInt(duartionElement.value); const duration = parseInt(duartionElement.value);
if (!isNumber(duration) || Number.isNaN(duration)) { if (!isNumber(duration) || Number.isNaN(duration)) {
throw new Error('duration can contain nubers only'); throw new Error("duration can contain nubers only");
} }
if (duration <= 0) { if (duration <= 0) {
throw new Error('duration must be greater than zero'); throw new Error("duration must be greater than zero");
} }
return duration; return duration;
}; };

View File

@ -15,10 +15,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import addLevelButtonAddEventListener from './addLevelButton'; import addLevelButtonAddEventListener from "./addLevelButton";
import addSubmitEventListener from './form'; import addSubmitEventListener from "./form";
export const index = () => { export const index = (): void => {
addLevelButtonAddEventListener(); addLevelButtonAddEventListener();
addSubmitEventListener(); addSubmitEventListener();
}; };

View File

@ -15,27 +15,27 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import getLevelFields from './getLevelFields'; import getLevelFields from "./getLevelFields";
import { import {
getAddForm, getAddForm,
level1, level1,
level2, level2,
fillAddLevel, fillAddLevel,
addLevel, addLevel,
} from '../setupTests'; } from "../setupTests";
document.body.innerHTML = getAddForm(); document.body.innerHTML = getAddForm();
const visNumErr = 'visitor can contain nubers only'; const visNumErr = "visitor can contain nubers only";
const diffNumErr = 'difficulty can contain nubers only'; const diffNumErr = "difficulty can contain nubers only";
it('get levels fields works', () => { it("get levels fields works", () => {
addLevel(level1.visitor_threshold, level1.difficulty_factor); addLevel(level1.visitor_threshold, level1.difficulty_factor);
expect(getLevelFields(1)).toEqual(level1); expect(getLevelFields(1)).toEqual(level1);
// NaN visitor // NaN visitor
try { try {
fillAddLevel('test', level2.difficulty_factor); fillAddLevel("test", level2.difficulty_factor);
getLevelFields(2); getLevelFields(2);
} catch (e) { } catch (e) {
expect(e.message).toBe(visNumErr); expect(e.message).toBe(visNumErr);
@ -43,7 +43,7 @@ it('get levels fields works', () => {
// Nan difficulty_factor // Nan difficulty_factor
try { try {
fillAddLevel(level2.visitor_threshold, 'fooasdads'); fillAddLevel(level2.visitor_threshold, "fooasdads");
getLevelFields(2); getLevelFields(2);
} catch (e) { } catch (e) {
expect(e.message).toBe(diffNumErr); expect(e.message).toBe(diffNumErr);

View File

@ -15,13 +15,13 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {Level} from './index'; import { Level } from "./index";
import CONST from '../const'; import CONST from "../const";
import log from '../../../../../logger'; import log from "../../../../../logger";
/** Fetches level from DOM using the ID passesd and validates */ /** Fetches level from DOM using the ID passesd and validates */
const getLevelFields = (id: number) => { const getLevelFields = (id: number): Level => {
log.debug(`[getLevelFields]: id: ${id}`); log.debug(`[getLevelFields]: id: ${id}`);
const visitorID = CONST.VISITOR_WITHOUT_LEVEL + id.toString(); const visitorID = CONST.VISITOR_WITHOUT_LEVEL + id.toString();
const difficultyID = CONST.DIFFICULTY_WITHOUT_LEVEL + id.toString(); const difficultyID = CONST.DIFFICULTY_WITHOUT_LEVEL + id.toString();
@ -35,11 +35,11 @@ const getLevelFields = (id: number) => {
const difficulty_factor = parseInt(difficultyElement.value); const difficulty_factor = parseInt(difficultyElement.value);
if (Number.isNaN(visitor_threshold)) { if (Number.isNaN(visitor_threshold)) {
throw new Error('visitor can contain nubers only'); throw new Error("visitor can contain nubers only");
} }
if (Number.isNaN(difficulty_factor)) { if (Number.isNaN(difficulty_factor)) {
throw new Error('difficulty can contain nubers only'); throw new Error("difficulty can contain nubers only");
} }
const level: Level = { const level: Level = {
@ -48,7 +48,7 @@ const getLevelFields = (id: number) => {
}; };
log.debug( log.debug(
`[getLevelFields.ts] visitor: ${visitor_threshold} difficulty: ${difficulty_factor}`, `[getLevelFields.ts] visitor: ${visitor_threshold} difficulty: ${difficulty_factor}`
); );
return level; return level;

View File

@ -15,12 +15,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import getNumLevels from './getNumLevels'; import getNumLevels from "./getNumLevels";
import {getAddForm, addLevel} from '../setupTests'; import {getAddForm, addLevel} from "../setupTests";
document.body.innerHTML = getAddForm(); document.body.innerHTML = getAddForm();
it('get num levels works', () => { it("get num levels works", () => {
expect(getNumLevels()).toBe(1); expect(getNumLevels()).toBe(1);
addLevel(2, 4); addLevel(2, 4);
expect(getNumLevels()).toBe(2); expect(getNumLevels()).toBe(2);

View File

@ -15,16 +15,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import CONST from '../const'; import CONST from "../const";
import log from '../../../../../logger'; import log from "../../../../../logger";
/** returns number of level input fields currently in DOM */ /** returns number of level input fields currently in DOM */
const getNumLevels = () => { const getNumLevels = (): number => {
let numLevels = 0; let numLevels = 0;
document document
.querySelectorAll(`.${CONST.LEVEL_CONTAINER_CLASS}`) .querySelectorAll(`.${CONST.LEVEL_CONTAINER_CLASS}`)
.forEach(_ => numLevels++); .forEach(() => numLevels++);
log.debug(`[getNumLevels]: numLevels: ${numLevels}`); log.debug(`[getNumLevels]: numLevels: ${numLevels}`);
return numLevels; return numLevels;
}; };

View File

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import log from '../../../../../logger'; import log from "../../../../../logger";
/** Datatype represenging an mCaptcha level */ /** Datatype represenging an mCaptcha level */
export type Level = { export type Level = {
@ -95,7 +95,7 @@ export const LEVELS = (function() {
} }
} }
levels.levels = tmpLevel.levels; levels.levels = tmpLevel.levels;
log.debug(`post update:`); log.debug("post update:");
LEVELS.print(); LEVELS.print();
return true; return true;
} catch (e) { } catch (e) {
@ -133,7 +133,7 @@ export const LEVELS = (function() {
} }
} }
levels.levels = tmpLevel.levels; levels.levels = tmpLevel.levels;
log.debug('Post remove:'); log.debug("Post remove:");
LEVELS.print(); LEVELS.print();
return true; return true;
} catch (e) { } catch (e) {

View File

@ -15,14 +15,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {LEVELS, Level} from './index'; import {LEVELS, Level} from "./index";
import {level1, level1visErr, level1diffErr, level2} from '../setupTests'; import {level1, level1visErr, level1diffErr, level2} from "../setupTests";
const visitorErr = 'visitor count should be greater than previous levels'; const visitorErr = "visitor count should be greater than previous levels";
const difficultyErr = 'difficulty should be greater than previous levels'; const difficultyErr = "difficulty should be greater than previous levels";
const zeroVisError = 'visitors must be greater than zero'; const zeroVisError = "visitors must be greater than zero";
const zeroDiffError = 'difficulty must be greater than zero'; const zeroDiffError = "difficulty must be greater than zero";
const zeroVis: Level = { const zeroVis: Level = {
difficulty_factor: 10, difficulty_factor: 10,
@ -34,7 +34,7 @@ const zeroDiff: Level = {
visitor_threshold: 10, visitor_threshold: 10,
}; };
it('LEVELS works', () => { it("LEVELS works", () => {
// add level // add level
LEVELS.add(level1); LEVELS.add(level1);
expect(LEVELS.getLevels()).toEqual([level1]); expect(LEVELS.getLevels()).toEqual([level1]);

View File

@ -15,14 +15,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import CONST from '../const'; import CONST from "../const";
import getLevelFields from './getLevelFields'; import getLevelFields from "./getLevelFields";
import {LEVELS} from './index'; import { LEVELS } from "./index";
import createError from '../../../../../components/error'; import createError from "../../../../../components/error";
/** on-change event handler to update level */ /** on-change event handler to update level */
const updateLevel = (e: Event) => { const updateLevel = (e: Event): void => {
const target = <HTMLInputElement>e.target; const target = <HTMLInputElement>e.target;
const id = target.id; const id = target.id;
@ -36,7 +36,7 @@ const updateLevel = (e: Event) => {
} }
if (Number.isNaN(level)) { if (Number.isNaN(level)) {
console.error(`[updateLevel.ts] level # computed is not correct, got NaN`); console.error("[updateLevel.ts] level # computed is not correct, got NaN");
} }
try { try {
@ -48,7 +48,7 @@ const updateLevel = (e: Event) => {
}; };
/** registers on-change event handlers to update levels */ /** registers on-change event handlers to update levels */
export const register = (id: number) => { export const register = (id: number): void => {
const visitorID = CONST.VISITOR_WITHOUT_LEVEL + id.toString(); const visitorID = CONST.VISITOR_WITHOUT_LEVEL + id.toString();
const difficultyID = CONST.DIFFICULTY_WITHOUT_LEVEL + id.toString(); const difficultyID = CONST.DIFFICULTY_WITHOUT_LEVEL + id.toString();
@ -57,6 +57,6 @@ export const register = (id: number) => {
document.getElementById(difficultyID) document.getElementById(difficultyID)
); );
visitorElement.addEventListener('input', updateLevel, false); visitorElement.addEventListener("input", updateLevel, false);
difficultyElement.addEventListener('input', updateLevel, false); difficultyElement.addEventListener("input", updateLevel, false);
}; };

View File

@ -15,15 +15,15 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import validateLevel from './validateLevel'; import validateLevel from "./validateLevel";
import {getAddForm, level1, fillAddLevel} from '../setupTests'; import {getAddForm, level1, fillAddLevel} from "../setupTests";
import setup from '../../../../../components/error/setUpTests'; import setup from "../../../../../components/error/setUpTests";
document.body.innerHTML = getAddForm(); document.body.innerHTML = getAddForm();
document.body.appendChild(setup()); document.body.appendChild(setup());
it('validate levels fields works', () => { it("validate levels fields works", () => {
// null error // null error
expect(validateLevel(1)).toEqual(false); expect(validateLevel(1)).toEqual(false);

View File

@ -15,15 +15,15 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {LEVELS} from './index'; import {LEVELS} from "./index";
import getLevelFields from './getLevelFields'; import getLevelFields from "./getLevelFields";
import createError from '../../../../../components/error/'; import createError from "../../../../../components/error/";
/** /**
* Fetches level from DOM using the ID passesd and validates * Fetches level from DOM using the ID passesd and validates
* its contents * its contents
* */ * */
const validateLevel = (id: number) => { const validateLevel = (id: number): boolean => {
try { try {
const level = getLevelFields(id); const level = getLevelFields(id);
LEVELS.add(level); LEVELS.add(level);

View File

@ -14,13 +14,13 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {LEVELS} from '../levels/index'; import { LEVELS } from "../levels/index";
import updateLevelNumbersOnDOM from './updateDom'; import updateLevelNumbersOnDOM from "./updateDom";
import CONST from '../const'; import CONST from "../const";
import log from '../../../../../logger'; import log from "../../../../../logger";
const REMOVE_LEVEL_BUTTON = 'sitekey-form__level-remove-level-button'; const REMOVE_LEVEL_BUTTON = "sitekey-form__level-remove-level-button";
/** /**
* Gets executed when 'Remove' Button is clicked to remove levels * Gets executed when 'Remove' Button is clicked to remove levels
@ -31,12 +31,12 @@ const removeLevel = (e: Event) => {
const FIELDSET = <HTMLElement>PARENT.parentElement; const FIELDSET = <HTMLElement>PARENT.parentElement;
const levelNum = parseInt( const levelNum = parseInt(
eventTarget.id.slice(CONST.REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL.length), eventTarget.id.slice(CONST.REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL.length)
); );
if (Number.isNaN(levelNum)) { if (Number.isNaN(levelNum)) {
const msg = const msg =
'[removeLevelButton.ts] error in parsing level number from remove button ID'; "[removeLevelButton.ts] error in parsing level number from remove button ID";
//log.error(msg); //log.error(msg);
throw new Error(msg); throw new Error(msg);
} }
@ -47,19 +47,19 @@ const removeLevel = (e: Event) => {
}; };
/** adds onclick event listener */ /** adds onclick event listener */
export const addRemoveLevelButtonEventListener = (level: number) => { export const addRemoveLevelButtonEventListener = (level: number): void => {
const removeButton = document.getElementById( const removeButton = document.getElementById(
`${CONST.REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL}${level}`, `${CONST.REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL}${level}`
); );
removeButton.addEventListener('click', removeLevel); removeButton.addEventListener("click", removeLevel);
}; };
/** adds onclick event listener to all remove buttons */ /** adds onclick event listener to all remove buttons */
export const addRemoveLevelButtonEventListenerAll = () => { export const addRemoveLevelButtonEventListenerAll = (): void => {
const removeButtons = document.querySelectorAll(`.${REMOVE_LEVEL_BUTTON}`); const removeButtons = document.querySelectorAll(`.${REMOVE_LEVEL_BUTTON}`);
removeButtons.forEach(button => removeButtons.forEach((button) =>
button.addEventListener('click', removeLevel), button.addEventListener("click", removeLevel)
); );
}; };
@ -67,20 +67,20 @@ export const addRemoveLevelButtonEventListenerAll = () => {
* Generate Remove button HTML. On-click handler should be added * Generate Remove button HTML. On-click handler should be added
* seprately * seprately
*/ */
export const getRemoveButtonHTML = (level: number) => { export const getRemoveButtonHTML = (level: number): HTMLLabelElement => {
log.log(`[generating HTML getHtml]level: ${level}`); log.log(`[generating HTML getHtml]level: ${level}`);
const btn = document.createElement('input'); const btn = document.createElement("input");
btn.className = CONST.REMOVE_LEVEL_BUTTON_CLASS; btn.className = CONST.REMOVE_LEVEL_BUTTON_CLASS;
btn.type = 'button'; btn.type = "button";
const id = `${CONST.REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL}${level}`; const id = `${CONST.REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL}${level}`;
btn.name = id; btn.name = id;
btn.id = id; btn.id = id;
btn.value = 'x'; btn.value = "x";
const removeLabel = document.createElement('label'); const removeLabel = document.createElement("label");
removeLabel.className = CONST.REMOVE_LEVEL_LABEL_CLASS; removeLabel.className = CONST.REMOVE_LEVEL_LABEL_CLASS;
const removeLabelText = document.createTextNode('RemoveLevel'); const removeLabelText = document.createTextNode("RemoveLevel");
removeLabel.appendChild(removeLabelText); removeLabel.appendChild(removeLabelText);
removeLabel.appendChild(btn); removeLabel.appendChild(btn);
removeLabel.htmlFor = id; removeLabel.htmlFor = id;

View File

@ -15,17 +15,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import getNumLevels from '../levels/getNumLevels'; import getNumLevels from "../levels/getNumLevels";
import { import { getAddForm, addLevel } from "../setupTests";
getAddForm, import CONST from "../const";
getRemoveButtonHTMLForm,
trim,
addLevel,
} from '../setupTests';
import CONST from '../const';
import log from '../../../../../logger'; import log from "../../../../../logger";
import {MODE} from '../../../../../logger'; import { MODE } from "../../../../../logger";
document.body.innerHTML = getAddForm(); document.body.innerHTML = getAddForm();
@ -46,13 +41,13 @@ const setUp = () => {
log.setMode(MODE.none); log.setMode(MODE.none);
it('removeLevelButton works', () => { it("removeLevelButton works", () => {
setUp(); setUp();
for (let i = 1; i < 4; i++) { for (let i = 1; i < 4; i++) {
const l1 = <HTMLButtonElement>( const l1 = <HTMLButtonElement>(
document.getElementById( document.getElementById(
`${CONST.REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL}${1}`, `${CONST.REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL}${1}`
) )
); );

View File

@ -14,25 +14,25 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import getNumLevels from '../../levels/getNumLevels'; import getNumLevels from "../../levels/getNumLevels";
import CONST from '../../const'; import CONST from "../../const";
import log from '../../../../../../logger'; import log from "../../../../../../logger";
import updateLabels from './updateLabel'; import updateLabels from "./updateLabel";
import updateInputs from './updateInputs'; import updateInputs from "./updateInputs";
import updateRemoveButton from './updateRemoveButton'; import updateRemoveButton from "./updateRemoveButton";
import updateLevelGroup from './updateLevelGroup'; import updateLevelGroup from "./updateLevelGroup";
/** /**
* update level number on fieldset legends and their ids too * update level number on fieldset legends and their ids too
* @param {number} id - level number that was ordered to remove. * @param {number} id - level number that was ordered to remove.
* All updates are made relative to id * All updates are made relative to id
* */ * */
const updateLevelNumbersOnDOM = (id: number) => { const updateLevelNumbersOnDOM = (id: number): void => {
const numLevels = getNumLevels(); const numLevels = getNumLevels();
if (id == numLevels) { if (id == numLevels) {
throw new Error( throw new Error(
"Can't remove the very fist element, it has to be first added to DOM", "Can't remove the very fist element, it has to be first added to DOM"
); );
} }
@ -42,7 +42,7 @@ const updateLevelNumbersOnDOM = (id: number) => {
const newLevel = i - 1; const newLevel = i - 1;
const levelGroup = document.querySelector( const levelGroup = document.querySelector(
`#${CONST.LEVEL_FIELDSET_ID_WITHOUT_LEVEL}${i}`, `#${CONST.LEVEL_FIELDSET_ID_WITHOUT_LEVEL}${i}`
); );
if (levelGroup === null) { if (levelGroup === null) {
@ -53,9 +53,9 @@ const updateLevelNumbersOnDOM = (id: number) => {
} }
// rename legend // rename legend
const legend = levelGroup.getElementsByTagName('legend')[0]; const legend = levelGroup.getElementsByTagName("legend")[0];
const legendText = document.createTextNode(`Level ${newLevel}`); const legendText = document.createTextNode(`Level ${newLevel}`);
const newLegend = document.createElement('legend'); const newLegend = document.createElement("legend");
newLegend.className = legend.className; newLegend.className = legend.className;
newLegend.appendChild(legendText); newLegend.appendChild(legendText);
legend.replaceWith(newLegend); legend.replaceWith(newLegend);

View File

@ -15,12 +15,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import getNumLevels from '../../levels/getNumLevels'; import getNumLevels from "../../levels/getNumLevels";
import {getAddForm, addLevel} from '../../setupTests'; import { getAddForm, addLevel } from "../../setupTests";
document.body.innerHTML = getAddForm(); document.body.innerHTML = getAddForm();
export const setupAddlevels = () => { export const setupAddlevels = (): void => {
expect(getNumLevels()).toBe(1); expect(getNumLevels()).toBe(1);
// add a level // add a level
addLevel(2, 2); addLevel(2, 2);

View File

@ -15,20 +15,20 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {getAddForm, trim} from '../../setupTests'; import {getAddForm, trim} from "../../setupTests";
import updateInputs from './updateInputs'; import updateInputs from "./updateInputs";
import CONST from '../../const'; import CONST from "../../const";
import log from '../../../../../../logger'; import log from "../../../../../../logger";
import {MODE} from '../../../../../../logger'; import {MODE} from "../../../../../../logger";
import {setupAddlevels} from './setupTests'; import {setupAddlevels} from "./setupTests";
document.body.innerHTML = getAddForm(); document.body.innerHTML = getAddForm();
log.setMode(MODE.none); log.setMode(MODE.none);
it('updateInputs works', () => { it("updateInputs works", () => {
setupAddlevels(); setupAddlevels();
// removing level 2 // removing level 2
const level = 2; const level = 2;
@ -58,7 +58,7 @@ it('updateInputs works', () => {
}); });
/** get initial form to test remove button functionality */ /** get initial form to test remove button functionality */
export const update = () => { export const update = (): string => {
return ` return `
<form class="sitekey-form" action="/api/v1/mcaptcha/levels/add" method="post"> <form class="sitekey-form" action="/api/v1/mcaptcha/levels/add" method="post">
<h1 class="form__title"> <h1 class="form__title">

View File

@ -14,11 +14,11 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import CONST from '../../const'; import CONST from "../../const";
import log from '../../../../../../logger'; import log from "../../../../../../logger";
/** update input IDs with new level */ /** update input IDs with new level */
const updateInput = (levelGroup: Element, newLevel: number) => { const updateInput = (levelGroup: Element, newLevel: number): void => {
const inputs = <NodeListOf<HTMLInputElement>>( const inputs = <NodeListOf<HTMLInputElement>>(
levelGroup.querySelectorAll(`.${CONST.LEVEL_INPUT_CLASS}`) levelGroup.querySelectorAll(`.${CONST.LEVEL_INPUT_CLASS}`)
); );
@ -26,17 +26,17 @@ const updateInput = (levelGroup: Element, newLevel: number) => {
inputs.forEach(input => { inputs.forEach(input => {
if (input.id.includes(CONST.VISITOR_WITHOUT_LEVEL)) { if (input.id.includes(CONST.VISITOR_WITHOUT_LEVEL)) {
log.log(`${input.id}`); log.log(`${input.id}`);
log.log('changing visitor_threshold input'); log.log("changing visitor_threshold input");
const id = `${CONST.VISITOR_WITHOUT_LEVEL}${newLevel}`; const id = `${CONST.VISITOR_WITHOUT_LEVEL}${newLevel}`;
input.id = id; input.id = id;
input.name = id; input.name = id;
} else if (input.id.includes(CONST.DIFFICULTY_WITHOUT_LEVEL)) { } else if (input.id.includes(CONST.DIFFICULTY_WITHOUT_LEVEL)) {
log.log('changing difficulty input'); log.log("changing difficulty input");
const id = `${CONST.DIFFICULTY_WITHOUT_LEVEL}${newLevel}`; const id = `${CONST.DIFFICULTY_WITHOUT_LEVEL}${newLevel}`;
input.id = id; input.id = id;
input.name = id; input.name = id;
} else { } else {
if (input.id != 'add') { if (input.id != "add") {
throw new Error(`Did you add an extra input to DOM? ${input.id} ${input.className} ${input.name}`); throw new Error(`Did you add an extra input to DOM? ${input.id} ${input.className} ${input.name}`);
} }
} }

View File

@ -15,18 +15,15 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import getNumLevels from '../../levels/getNumLevels'; import { trim } from "../../setupTests";
import {getAddForm, trim} from '../../setupTests'; import updateLabels from "./updateLabel";
import updateLabels from './updateLabel'; import CONST from "../../const";
import CONST from '../../const';
import log from '../../../../../../logger'; import log from "../../../../../../logger";
import {MODE} from '../../../../../../logger'; import { MODE } from "../../../../../../logger";
import {setupAddlevels} from './setupTests';
/** get initial form to test remove button functionality */ /** get initial form to test remove button functionality */
export const labelLevel = (level: number) => { export const labelLevel = (level: number): string => {
return ` return `
<form class="sitekey-form" action="/api/v1/mcaptcha/levels/add" method="post"> <form class="sitekey-form" action="/api/v1/mcaptcha/levels/add" method="post">
<fieldset class="sitekey__level-container" id="level-group-2"> <fieldset class="sitekey__level-container" id="level-group-2">
@ -85,11 +82,11 @@ document.body.innerHTML = labelLevel(2);
log.setMode(MODE.none); log.setMode(MODE.none);
it('addLevelButton works', () => { it("addLevelButton works", () => {
// removing level 2 // removing level 2
const level = 2; const level = 2;
const levelGroup = document.querySelector( const levelGroup = document.querySelector(
`#${CONST.LEVEL_FIELDSET_ID_WITHOUT_LEVEL}${level}`, `#${CONST.LEVEL_FIELDSET_ID_WITHOUT_LEVEL}${level}`
); );
const newLevel = 20; const newLevel = 20;
@ -100,22 +97,22 @@ it('addLevelButton works', () => {
levelGroup.querySelectorAll(`.${CONST.LABEL_CLASS}`) levelGroup.querySelectorAll(`.${CONST.LABEL_CLASS}`)
); );
log.log(labels); log.log(labels);
labels.forEach(label => { labels.forEach((label) => {
log.log(`${label.htmlFor}`); log.log(`${label.htmlFor}`);
if (label.htmlFor.includes(CONST.VISITOR_WITHOUT_LEVEL)) { if (label.htmlFor.includes(CONST.VISITOR_WITHOUT_LEVEL)) {
expect(label.htmlFor).toBe(`${CONST.VISITOR_WITHOUT_LEVEL}${newLevel}`); expect(label.htmlFor).toBe(`${CONST.VISITOR_WITHOUT_LEVEL}${newLevel}`);
} else if (label.htmlFor.includes(CONST.DIFFICULTY_WITHOUT_LEVEL)) { } else if (label.htmlFor.includes(CONST.DIFFICULTY_WITHOUT_LEVEL)) {
expect(label.htmlFor).toBe( expect(label.htmlFor).toBe(
`${CONST.DIFFICULTY_WITHOUT_LEVEL}${newLevel}`, `${CONST.DIFFICULTY_WITHOUT_LEVEL}${newLevel}`
); );
} else if ( } else if (
label.htmlFor.includes(CONST.REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL) label.htmlFor.includes(CONST.REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL)
) { ) {
expect(label.htmlFor).toBe( expect(label.htmlFor).toBe(
`${CONST.REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL}${newLevel}`, `${CONST.REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL}${newLevel}`
); );
} else { } else {
throw new Error('Did you add an extra label to DOM?'); throw new Error("Did you add an extra label to DOM?");
} }
}); });

View File

@ -14,17 +14,17 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import CONST from '../../const'; import CONST from "../../const";
import log from '../../../../../../logger'; import log from "../../../../../../logger";
/** update level lables to match new level */ /** update level lables to match new level */
const updateLabels = (levelGroup: Element, newLevel: number) => { const updateLabels = (levelGroup: Element, newLevel: number): void => {
// rename labels // rename labels
const labels = <NodeListOf<HTMLLabelElement>>( const labels = <NodeListOf<HTMLLabelElement>>(
levelGroup.querySelectorAll(`label`) levelGroup.querySelectorAll("label")
); );
log.log(labels); log.log(labels);
labels.forEach(label => { labels.forEach((label) => {
log.log(`${label.htmlFor}`); log.log(`${label.htmlFor}`);
const currentFor = label.htmlFor; const currentFor = label.htmlFor;
if (currentFor.includes(CONST.VISITOR_WITHOUT_LEVEL)) { if (currentFor.includes(CONST.VISITOR_WITHOUT_LEVEL)) {
@ -36,9 +36,9 @@ const updateLabels = (levelGroup: Element, newLevel: number) => {
) { ) {
label.htmlFor = `${CONST.REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL}${newLevel}`; label.htmlFor = `${CONST.REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL}${newLevel}`;
} else { } else {
if (currentFor != 'add') { if (currentFor != "add") {
throw new Error( throw new Error(
`Did you add an extra label to DOM? Found label with for: ${currentFor}`, `Did you add an extra label to DOM? Found label with for: ${currentFor}`
); );
} }
} }

View File

@ -15,18 +15,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import getNumLevels from '../../levels/getNumLevels'; import { trim} from "../../setupTests";
import {getAddForm, trim} from '../../setupTests'; import updateLevelGroup from "./updateLevelGroup";
import updateLevelGroup from './updateLevelGroup'; import CONST from "../../const";
import CONST from '../../const';
import log from '../../../../../../logger'; import log from "../../../../../../logger";
import {MODE} from '../../../../../../logger'; import {MODE} from "../../../../../../logger";
import {setupAddlevels} from './setupTests';
/** get initial form to test remove button functionality */ /** get initial form to test remove button functionality */
export const labelLevel = (level: number) => { export const labelLevel = (level: number): string => {
return ` return `
<form class="sitekey-form" action="/api/v1/mcaptcha/levels/add" method="post"> <form class="sitekey-form" action="/api/v1/mcaptcha/levels/add" method="post">
<fieldset class="sitekey__level-container" id="level-group-${level}"> <fieldset class="sitekey__level-container" id="level-group-${level}">
@ -85,7 +83,7 @@ document.body.innerHTML = labelLevel(2);
log.setMode(MODE.none); log.setMode(MODE.none);
it('update levelGroup works', () => { it("update levelGroup works", () => {
// removing level 2 // removing level 2
const level = 2; const level = 2;
const levelGroup = document.querySelector( const levelGroup = document.querySelector(

View File

@ -14,10 +14,10 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import CONST from '../../const'; import CONST from "../../const";
/** update level grup to match new level */ /** update level grup to match new level */
const updateLevelGroup = (levelGroup: Element, newLevel: number) => const updateLevelGroup = (levelGroup: Element, newLevel: number): string =>
(levelGroup.id = `${CONST.LEVEL_FIELDSET_ID_WITHOUT_LEVEL}${newLevel}`); (levelGroup.id = `${CONST.LEVEL_FIELDSET_ID_WITHOUT_LEVEL}${newLevel}`);
export default updateLevelGroup; export default updateLevelGroup;

View File

@ -15,15 +15,15 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {trim} from '../../setupTests'; import {trim} from "../../setupTests";
import updateRemoveButton from './updateRemoveButton'; import updateRemoveButton from "./updateRemoveButton";
import CONST from '../../const'; import CONST from "../../const";
import log from '../../../../../../logger'; import log from "../../../../../../logger";
import {MODE} from '../../../../../../logger'; import {MODE} from "../../../../../../logger";
/** get initial form to test remove button functionality */ /** get initial form to test remove button functionality */
export const labelLevel = (level: number) => { export const labelLevel = (level: number): string => {
return ` return `
<form class="sitekey-form" action="/api/v1/mcaptcha/levels/add" method="post"> <form class="sitekey-form" action="/api/v1/mcaptcha/levels/add" method="post">
<fieldset class="sitekey__level-container" id="level-group-"> <fieldset class="sitekey__level-container" id="level-group-">
@ -83,7 +83,7 @@ document.body.innerHTML = labelLevel(level);
log.setMode(MODE.none); log.setMode(MODE.none);
it('update remove button works', () => { it("update remove button works", () => {
// removing level 2 // removing level 2
const levelGroup = document.getElementById( const levelGroup = document.getElementById(

View File

@ -14,10 +14,10 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import CONST from '../../const'; import CONST from "../../const";
/** update remove level button's ID */ /** update remove level button's ID */
const updateRemoveButton = (levelGroup: Element, newLevel: number) => { const updateRemoveButton = (levelGroup: Element, newLevel: number): void => {
// rename button // rename button
const button = <HTMLInputElement>( const button = <HTMLInputElement>(
levelGroup.querySelector(`.${CONST.REMOVE_LEVEL_BUTTON_CLASS}`) levelGroup.querySelector(`.${CONST.REMOVE_LEVEL_BUTTON_CLASS}`)

View File

@ -14,13 +14,13 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import getNumLevels from './levels/getNumLevels'; import getNumLevels from "./levels/getNumLevels";
import {Level} from './levels/index'; import { Level } from "./levels/index";
import CONST from './const'; import CONST from "./const";
import addLevelButtonAddEventListener from './addLevelButton'; import addLevelButtonAddEventListener from "./addLevelButton";
/** get rid of all whitespaces, useful when comparing DOM states */ /** get rid of all whitespaces, useful when comparing DOM states */
export const trim = (s: string) => s.replace(/\s/g, ''); export const trim = (s: string): string => s.replace(/\s/g, "");
export const level1: Level = { export const level1: Level = {
difficulty_factor: 200, difficulty_factor: 200,
@ -43,7 +43,7 @@ export const level2: Level = {
}; };
/** add level to DOM by filling add level form and clicking "Add" button */ /** add level to DOM by filling add level form and clicking "Add" button */
export const addLevel = (visitor: number, diff: number) => { export const addLevel = (visitor: number, diff: number): void => {
fillAddLevel(visitor, diff); fillAddLevel(visitor, diff);
const addLevelButton = <HTMLElement>( const addLevelButton = <HTMLElement>(
document.querySelector(`.${CONST.ADD_LEVEL_BUTTON}`) document.querySelector(`.${CONST.ADD_LEVEL_BUTTON}`)
@ -54,8 +54,8 @@ export const addLevel = (visitor: number, diff: number) => {
/** Fill add level form without clicking add button */ /** Fill add level form without clicking add button */
export const fillAddLevel = ( export const fillAddLevel = (
visitor: number | string, visitor: number | string,
diff: number | string, diff: number | string
) => { ): void => {
addLevelButtonAddEventListener(); addLevelButtonAddEventListener();
const level = getNumLevels(); const level = getNumLevels();
@ -71,7 +71,11 @@ export const fillAddLevel = (
}; };
/** Fill add level form without clicking add button */ /** Fill add level form without clicking add button */
export const editLevel = (level: number, visitor?: number, diff?: number) => { export const editLevel = (
level: number,
visitor?: number,
diff?: number
): void => {
if (visitor !== undefined) { if (visitor !== undefined) {
const visitorField = <HTMLInputElement>( const visitorField = <HTMLInputElement>(
document.getElementById(`${CONST.VISITOR_WITHOUT_LEVEL}${level}`) document.getElementById(`${CONST.VISITOR_WITHOUT_LEVEL}${level}`)
@ -88,18 +92,18 @@ export const editLevel = (level: number, visitor?: number, diff?: number) => {
}; };
/** Fill description in add level form */ /** Fill description in add level form */
export const fillDescription = (description: string) => { export const fillDescription = (description: string): void => {
const inputElement = <HTMLInputElement>document.getElementById('description'); const inputElement = <HTMLInputElement>document.getElementById("description");
inputElement.value = description; inputElement.value = description;
}; };
/** Fill duration in add level form */ /** Fill duration in add level form */
export const fillDuration = (duration: number | string) => { export const fillDuration = (duration: number | string): void => {
const inputElement = <HTMLInputElement>document.getElementById('duration'); const inputElement = <HTMLInputElement>document.getElementById("duration");
inputElement.value = duration.toString(); inputElement.value = duration.toString();
}; };
export const getAddForm = () => ` export const getAddForm = (): string => `
<form class="sitekey-form" action="/api/v1/mcaptcha/levels/add" method="post"> <form class="sitekey-form" action="/api/v1/mcaptcha/levels/add" method="post">
<h1 class="form__title"> <h1 class="form__title">
Add Sitekey Add Sitekey
@ -171,7 +175,7 @@ export const getAddForm = () => `
`; `;
/** get initial form to test remove button functionality */ /** get initial form to test remove button functionality */
export const getRemoveButtonHTMLForm = () => { export const getRemoveButtonHTMLForm = (): string => {
return ` return `
<form class="sitekey-form" action="/api/v1/mcaptcha/levels/add" method="post"> <form class="sitekey-form" action="/api/v1/mcaptcha/levels/add" method="post">
<h1 class="form__title"> <h1 class="form__title">

View File

@ -15,16 +15,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {getPassword} from '../../../auth/login/ts/'; import { getPassword } from "../../../auth/login/ts/";
import FORM from '../../../auth/sudo/'; import FORM from "../../../auth/sudo/";
import additionalData from '../../../components/additional-data'; import additionalData from "../../../components/additional-data";
import registerShowPassword from '../../../components/showPassword'; import registerShowPassword from "../../../components/showPassword";
import getFormUrl from '../../../utils/getFormUrl'; import getFormUrl from "../../../utils/getFormUrl";
import genJsonPayload from '../../../utils/genJsonPayload'; import genJsonPayload from "../../../utils/genJsonPayload";
import createError from '../../../components/error'; import createError from "../../../components/error";
import VIEWS from '../../../views/v1/routes'; import VIEWS from "../../../views/v1/routes";
const submit = async (e: Event) => { const submit = async (e: Event) => {
e.preventDefault(); e.preventDefault();
@ -47,7 +47,7 @@ const submit = async (e: Event) => {
} }
}; };
export const index = () => { export const index = (): void => {
FORM.get().addEventListener('submit', submit, true); FORM.get().addEventListener("submit", submit, true);
registerShowPassword(); registerShowPassword();
}; };

View File

@ -15,17 +15,17 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import getNumLevels from '../add/ts/levels/getNumLevels'; import getNumLevels from "../add/ts/levels/getNumLevels";
import {addLevel} from '../add/ts/setupTests'; import {addLevel} from "../add/ts/setupTests";
import setup from '../../../components/error/setUpTests'; import setup from "../../../components/error/setUpTests";
import * as SETUP from './setupTest'; import * as SETUP from "./setupTest";
document.body.innerHTML = SETUP.EDIT_FORM; document.body.innerHTML = SETUP.EDIT_FORM;
document.body.appendChild(setup()); document.body.appendChild(setup());
jest.useFakeTimers(); jest.useFakeTimers();
it('edit sitekey works', () => { it("edit sitekey works", () => {
expect(getNumLevels()).toBe(2); expect(getNumLevels()).toBe(2);
// add a level // add a level
addLevel(5, 6); addLevel(5, 6);

View File

@ -14,31 +14,31 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import * as Add from '../add/ts/form/'; import * as Add from "../add/ts/form/";
import addLevelButtonAddEventListener from '../add/ts/addLevelButton'; import addLevelButtonAddEventListener from "../add/ts/addLevelButton";
import {addRemoveLevelButtonEventListenerAll} from '../add/ts/removeLevelButton'; import { addRemoveLevelButtonEventListenerAll } from "../add/ts/removeLevelButton";
import getNumLevels from '../add/ts/levels/getNumLevels'; import getNumLevels from "../add/ts/levels/getNumLevels";
import validateLevel from '../add/ts/levels/validateLevel'; import validateLevel from "../add/ts/levels/validateLevel";
import * as UpdateLevel from '../add/ts/levels/updateLevel'; import * as UpdateLevel from "../add/ts/levels/updateLevel";
import validateDescription from '../add/ts/form/validateDescription'; import validateDescription from "../add/ts/form/validateDescription";
import validateDuration from '../add/ts/form/validateDuration'; import validateDuration from "../add/ts/form/validateDuration";
import {LEVELS} from '../add/ts/levels'; import { LEVELS } from "../add/ts/levels";
import getFormUrl from '../../../utils/getFormUrl'; import getFormUrl from "../../../utils/getFormUrl";
import genJsonPayload from '../../../utils/genJsonPayload'; import genJsonPayload from "../../../utils/genJsonPayload";
import createError from '../../../components/error'; import createError from "../../../components/error";
import LazyElement from '../../../utils/lazyElement'; import LazyElement from "../../../utils/lazyElement";
import VIEWS from '../../../views/v1/routes'; import VIEWS from "../../../views/v1/routes";
const BTN_ID = 'sitekey-form__submit'; const BTN_ID = "sitekey-form__submit";
const BTN = new LazyElement(BTN_ID); const BTN = new LazyElement(BTN_ID);
const submit = async (e: Event) => { const submit = async (e: Event) => {
e.preventDefault(); e.preventDefault();
const description = validateDescription(e); const description = validateDescription(e);
const duration = validateDuration(e); const duration = validateDuration();
const formUrl = getFormUrl(Add.FORM); const formUrl = getFormUrl(Add.FORM);
@ -66,7 +66,7 @@ const submit = async (e: Event) => {
}; };
const addSubmitEventListener = () => { const addSubmitEventListener = () => {
Add.FORM.addEventListener('submit', submit, true); Add.FORM.addEventListener("submit", submit, true);
}; };
const bootstrapLevels = () => { const bootstrapLevels = () => {
@ -78,7 +78,7 @@ const bootstrapLevels = () => {
} }
}; };
export const index = () => { export const index = (): void => {
addSubmitEventListener(); addSubmitEventListener();
addLevelButtonAddEventListener(); addLevelButtonAddEventListener();
bootstrapLevels(); bootstrapLevels();

View File

@ -14,17 +14,17 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import CopyIcon from '../../../../components/clipboard/'; import CopyIcon from "../../../../components/clipboard/";
const SITEKEY_COPY_ICON = `sitekey__copy-icon`; const SITEKEY_COPY_ICON = "sitekey__copy-icon";
const SITEKEY_COPY_DONE_ICON = `sitekey__copy-done-icon`; const SITEKEY_COPY_DONE_ICON = "sitekey__copy-done-icon";
export const index = () => { export const index = (): void => {
const image = document.querySelectorAll(`.${SITEKEY_COPY_ICON}`); const image = document.querySelectorAll(`.${SITEKEY_COPY_ICON}`);
image.forEach((img: HTMLElement) => { image.forEach((img: HTMLElement) => {
if (!img.classList.contains(SITEKEY_COPY_ICON)) { if (!img.classList.contains(SITEKEY_COPY_ICON)) {
throw new Error( throw new Error(
'This method should only be called when sitekey copy button/icon is clicked', "This method should only be called when sitekey copy button/icon is clicked"
); );
} }
const sitekey = img.dataset.sitekey; const sitekey = img.dataset.sitekey;

View File

@ -14,4 +14,4 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
export const index = () => {}; //export const index = () => {};

View File

@ -15,8 +15,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import * as listSitekeys from '../sitekey/list/ts/'; import * as listSitekeys from "../sitekey/list/ts/";
export const index = () => { export const index = (): void => listSitekeys.index();
listSitekeys.index();
};

View File

@ -15,29 +15,29 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {Router} from './router'; import {Router} from "./router";
'use strict'; "use strict";
const result = { const result = {
result: '', result: "",
}; };
const panelResult = 'hello from panel'; const panelResult = "hello from panel";
const panelRoute = '/panel'; const panelRoute = "/panel";
const panel = () => (result.result = panelResult); const panel = () => (result.result = panelResult);
const settingsRoute = '/sitekey/'; const settingsRoute = "/sitekey/";
const settingsResult = 'hello from settings'; const settingsResult = "hello from settings";
const settings = () => (result.result = settingsResult); const settings = () => (result.result = settingsResult);
const patternRoute = '/sitekey/[A-Z,a-z,0-9,_]+/'; const patternRoute = "/sitekey/[A-Z,a-z,0-9,_]+/";
const examplePatternRoute = '/sitekey/alksdjakdjadajkhdjahrjke234/'; const examplePatternRoute = "/sitekey/alksdjakdjadajkhdjahrjke234/";
const patterResult = 'hello from pattern route'; const patterResult = "hello from pattern route";
const pattern = () => (result.result = patterResult); const pattern = () => (result.result = patterResult);
const UriExistsErr = 'URI exists'; const UriExistsErr = "URI exists";
const emptyUriErr = 'uri is empty'; const emptyUriErr = "uri is empty";
const unregisteredRouteErr = "Route isn't registered"; const unregisteredRouteErr = "Route isn't registered";
const router = new Router(); const router = new Router();
@ -45,23 +45,23 @@ router.register(patternRoute, pattern);
router.register(panelRoute, panel); router.register(panelRoute, panel);
router.register(settingsRoute, settings); router.register(settingsRoute, settings);
it('checks if Router works', () => { it("checks if Router works", () => {
window.history.pushState({}, '', examplePatternRoute); window.history.pushState({}, "", examplePatternRoute);
router.route(); router.route();
expect(result.result).toBe(patterResult); expect(result.result).toBe(patterResult);
window.history.pushState( window.history.pushState(
{}, {},
'', "",
examplePatternRoute.slice(0, examplePatternRoute.length - 1), examplePatternRoute.slice(0, examplePatternRoute.length - 1),
); );
router.route(); router.route();
expect(result.result).toBe(patterResult); expect(result.result).toBe(patterResult);
window.history.pushState({}, 'Settings', settingsRoute); window.history.pushState({}, "Settings", settingsRoute);
router.route(); router.route();
expect(result.result).toBe(settingsResult); expect(result.result).toBe(settingsResult);
window.history.pushState({}, 'Panel', panelRoute); window.history.pushState({}, "Panel", panelRoute);
router.route(); router.route();
expect(result.result).toBe(panelResult); expect(result.result).toBe(panelResult);
@ -74,14 +74,14 @@ it('checks if Router works', () => {
// empty URI registration // empty URI registration
try { try {
router.register(' ', settings); router.register(" ", settings);
} catch (e) { } catch (e) {
expect(e.message).toBe(emptyUriErr); expect(e.message).toBe(emptyUriErr);
} }
// routing to unregistered route // routing to unregistered route
try { try {
window.history.pushState({}, `Page Doesn't Exist`, `/page/doesnt/exist`); window.history.pushState({}, "Page Doesn't Exist", "/page/doesnt/exist");
router.route(); router.route();
} catch (e) { } catch (e) {
expect(e.message).toBe(unregisteredRouteErr); expect(e.message).toBe(unregisteredRouteErr);
@ -89,7 +89,7 @@ it('checks if Router works', () => {
// routing to unregistered route // routing to unregistered route
try { try {
window.history.pushState({}, `Page Doesn't Exist`, `/sitekey/;asd;lasdj`); window.history.pushState({}, "Page Doesn't Exist", "/sitekey/;asd;lasdj");
router.route(); router.route();
} catch (e) { } catch (e) {
expect(e.message).toBe(unregisteredRouteErr); expect(e.message).toBe(unregisteredRouteErr);

View File

@ -19,11 +19,11 @@
const normalizeUri = (uri: string) => { const normalizeUri = (uri: string) => {
uri = uri.trim(); uri = uri.trim();
if (uri.length == 0) { if (uri.length == 0) {
throw new Error('uri is empty'); throw new Error("uri is empty");
} }
let uriLength = uri.length; const uriLength = uri.length;
if (uri[uriLength - 1] == '/') { if (uri[uriLength - 1] == "/") {
uri = uri.slice(0, uriLength - 1); uri = uri.slice(0, uriLength - 1);
} }
return uri; return uri;
@ -51,14 +51,14 @@ export class Router {
* @param {function} fn: - function to be registered when window.locatin.path * @param {function} fn: - function to be registered when window.locatin.path
* matches uri * matches uri
* */ * */
register(uri: string, fn: () => void) { register(uri: string, fn: () => void): void {
uri = normalizeUri(uri); uri = normalizeUri(uri);
let pattern = new RegExp(`^${uri}$`); const pattern = new RegExp(`^${uri}$`);
let patterString = pattern.toString(); const patterString = pattern.toString();
if ( if (
this.routes.find(route => { this.routes.find((route) => {
if (route.pattern.toString() == patterString) { if (route.pattern.toString() == patterString) {
return true; return true;
} else { } else {
@ -66,7 +66,7 @@ export class Router {
} }
}) })
) { ) {
throw new Error('URI exists'); throw new Error("URI exists");
} }
const route: routeTuple = { const route: routeTuple = {
@ -80,13 +80,13 @@ export class Router {
* executes registered function with route * executes registered function with route
* matches window.pathname.location * matches window.pathname.location
* */ * */
route() { route(): void {
const path = normalizeUri(window.location.pathname); const path = normalizeUri(window.location.pathname);
let fn: undefined | (() => void); let fn: undefined | (() => void);
if ( if (
this.routes.find(route => { this.routes.find((route) => {
if (path.match(route.pattern)) { if (path.match(route.pattern)) {
fn = route.fn; fn = route.fn;
return true; return true;

View File

@ -16,7 +16,7 @@
*/ */
/** get login form HTML */ /** get login form HTML */
export const getLoginFormHtml = () => export const getLoginFormHtml = (): string =>
` `
<form method="POST" action="/something" id="form"> <form method="POST" action="/something" id="form">
<label class="form__in-group" for="username" <label class="form__in-group" for="username"
@ -51,7 +51,7 @@ export const getLoginFormHtml = () =>
`; `;
/** get registration form HTML */ /** get registration form HTML */
export const getRegistrationFormHtml = () => ` export const getRegistrationFormHtml = (): string => `
<form method="POST" action="/api/v1/signup" class="form__box" id="form"> <form method="POST" action="/api/v1/signup" class="form__box" id="form">
<label class="form__in-group" for="username" <label class="form__in-group" for="username"
>Username >Username
@ -104,7 +104,7 @@ export const getRegistrationFormHtml = () => `
</form> </form>
`; `;
export const mockAlert = () => { export const mockAlert = (): void => {
delete window.alert; delete window.alert;
window.alert = (x: any) => console.log(x); window.alert = (x: any) => console.log(x);

View File

@ -15,22 +15,22 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import genJsonPayload from './genJsonPayload'; import genJsonPayload from "./genJsonPayload";
'use strict'; "use strict";
const payload = { const payload = {
username: 'Jhon', username: "Jhon",
}; };
const value = { const value = {
method: 'POST', method: "POST",
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
body: JSON.stringify(payload), body: JSON.stringify(payload),
}; };
it('getFromUrl workds', () => { it("getFromUrl workds", () => {
expect(genJsonPayload(payload)).toEqual(value); expect(genJsonPayload(payload)).toEqual(value);
}); });

View File

@ -15,11 +15,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
const genJsonPayload = (payload: any) => { const genJsonPayload = (payload: object): object => {
const value = { const value = {
method: 'POST', method: "POST",
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
body: JSON.stringify(payload), body: JSON.stringify(payload),
}; };

View File

@ -15,27 +15,27 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import getFormUrl from './getFormUrl'; import getFormUrl from "./getFormUrl";
import {getLoginFormHtml} from '../setUpTests'; import {getLoginFormHtml} from "../setUpTests";
'use strict'; "use strict";
const formClassName = 'form__box'; const formClassName = "form__box";
const formURL = '/api/v1/signin'; const formURL = "/api/v1/signin";
const noFormErr = "Can't find form"; const noFormErr = "Can't find form";
document.body.innerHTML = getLoginFormHtml(); document.body.innerHTML = getLoginFormHtml();
const form = document.querySelector('form'); const form = document.querySelector("form");
form.action = formURL; form.action = formURL;
form.className = formClassName; form.className = formClassName;
it('getFromUrl workds', () => { it("getFromUrl workds", () => {
const name = `.${formClassName}`; const name = `.${formClassName}`;
expect(getFormUrl(name)).toContain(formURL); expect(getFormUrl(name)).toContain(formURL);
const form = <HTMLFormElement>document.querySelector('form'); const form = <HTMLFormElement>document.querySelector("form");
expect(getFormUrl(form)).toContain(formURL); expect(getFormUrl(form)).toContain(formURL);
expect(getFormUrl()).toContain(formURL); expect(getFormUrl()).toContain(formURL);

View File

@ -21,12 +21,12 @@
* So when using class-names, pass in ".whatever-classname" * So when using class-names, pass in ".whatever-classname"
* and for ID, "#id". * and for ID, "#id".
* */ * */
const getFormUrl = (querySelector?: string | HTMLFormElement) => { const getFormUrl = (querySelector?: string | HTMLFormElement): string => {
let form; let form;
if (querySelector === undefined) { if (querySelector === undefined) {
form = <HTMLFormElement>document.querySelector('form'); form = <HTMLFormElement>document.querySelector("form");
} }
if (typeof querySelector == 'string' || querySelector instanceof String) { if (typeof querySelector == "string" || querySelector instanceof String) {
form = <HTMLFormElement>document.querySelector(querySelector.toString()); form = <HTMLFormElement>document.querySelector(querySelector.toString());
} }
if (querySelector instanceof HTMLFormElement) { if (querySelector instanceof HTMLFormElement) {

View File

@ -15,22 +15,22 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import isBlankString from './isBlankString'; import isBlankString from "./isBlankString";
import {mockAlert} from '../setUpTests'; import {mockAlert} from "../setUpTests";
import setup from '../components/error/setUpTests'; import setup from "../components/error/setUpTests";
'use strict'; "use strict";
mockAlert(); mockAlert();
it('getFromUrl workds', () => { it("getFromUrl workds", () => {
document.querySelector('body').appendChild(setup()); document.querySelector("body").appendChild(setup());
expect(isBlankString('test', 'username')).toBe(false); expect(isBlankString("test", "username")).toBe(false);
try { try {
isBlankString(' ', 'username'); isBlankString(" ", "username");
} catch (e) { } catch (e) {
expect(e.message).toContain(`can't be empty`); expect(e.message).toContain("can't be empty");
} }
}); });

View File

@ -14,11 +14,11 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import createError from '../components/error/'; import createError from "../components/error/";
const isBlankString = (value: string|number, field: string, event?: Event) => { const isBlankString = (value: string|number, field: string, event?: Event): boolean => {
value = value.toString(); value = value.toString();
if (!value.replace(/\s/g, '').length) { if (!value.replace(/\s/g, "").length) {
if (event !== undefined) { if (event !== undefined) {
event.preventDefault(); event.preventDefault();
} }

View File

@ -15,14 +15,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import isNumber from './isNumber'; import isNumber from "./isNumber";
'use strict'; "use strict";
it('getFromUrl workds', () => { it("getFromUrl workds", () => {
expect(isNumber('test')).toBe(false); expect(isNumber("test")).toBe(false);
expect(isNumber('1test213')).toBe(false); expect(isNumber("1test213")).toBe(false);
expect(isNumber('12')).toBe(true); expect(isNumber("12")).toBe(true);
expect(isNumber(2)).toBe(true); expect(isNumber(2)).toBe(true);
}); });

View File

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
const isNumber = (value: string|number) => { const isNumber = (value: string|number): boolean => {
value = value.toString(); value = value.toString();
return /^\d+$/.test(value); return /^\d+$/.test(value);
}; };

View File

@ -22,7 +22,7 @@ class LazyElement {
this.id = id; this.id = id;
} }
get() { get(): HTMLElement {
if (this.element === null || this.element === undefined) { if (this.element === null || this.element === undefined) {
const element = document.getElementById(this.id); const element = document.getElementById(this.id);
if (element === null || element === undefined) { if (element === null || element === undefined) {

View File

@ -16,20 +16,20 @@
*/ */
const ROUTES = { const ROUTES = {
registerUser: '/join/', registerUser: "/join/",
loginUser: '/login/', loginUser: "/login/",
signoutUser: '/api/v1/signout', signoutUser: "/api/v1/signout",
panelHome: '/', panelHome: "/",
settings: '/settings/', settings: "/settings/",
updateSecret: '/settings/secret/update/', updateSecret: "/settings/secret/update/",
deleteAccount: '/settings/account/delete/', deleteAccount: "/settings/account/delete/",
docsHome: '/docs/', docsHome: "/docs/",
notifications: '/notifications', notifications: "/notifications",
listSitekey: '/sitekeys/', listSitekey: "/sitekeys/",
viewSitekey: (key: string) => `/sitekey/${key}/`, viewSitekey: (key: string): string => `/sitekey/${key}/`,
editSitekey: (key: string) => `/sitekey/${key}/edit/`, editSitekey: (key: string): string => `/sitekey/${key}/edit/`,
deleteSitekey: (key: string) => `/sitekey/${key}/delete/`, deleteSitekey: (key: string): string => `/sitekey/${key}/delete/`,
addSiteKey: '/sitekeys/add', addSiteKey: "/sitekeys/add",
}; };
export default ROUTES; export default ROUTES;

View File

@ -14,7 +14,7 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import './main.scss'; import "./main.scss";
//import prove from './runner/prove'; //import prove from './runner/prove';
//import fetchPoWConfig from './runner/fetchPoWConfig'; //import fetchPoWConfig from './runner/fetchPoWConfig';
//import sendWork from './runner/sendWork'; //import sendWork from './runner/sendWork';

View File

@ -8,19 +8,19 @@
* this program. If not, see <https://spdx.org/licenses/MIT.html> for * this program. If not, see <https://spdx.org/licenses/MIT.html> for
* MIT or <http://www.apache.org/licenses/LICENSE-2.0> for Apache. * MIT or <http://www.apache.org/licenses/LICENSE-2.0> for Apache.
*/ */
import LazyElement from '../../utils/lazyElement'; import LazyElement from "../../utils/lazyElement";
/** mcaptcha checkbox ID **/ /** mcaptcha checkbox ID **/
export const btnId = 'widget__verification-checkbox'; export const btnId = "widget__verification-checkbox";
/** get sitekey */ /** get sitekey */
export const sitekey = () => { export const sitekey = (): string => {
let sitekey; let sitekey;
return (() => { return (() => {
if (sitekey === null || sitekey === undefined) { if (sitekey === null || sitekey === undefined) {
sitekey = new URL(window.location.href).searchParams.get('sitekey'); sitekey = new URL(window.location.href).searchParams.get("sitekey");
if (sitekey === null || sitekey === undefined) { if (sitekey === null || sitekey === undefined) {
throw new Error(`Define sitekey in query parameter`); throw new Error("Define sitekey in query parameter");
} }
} }
return sitekey; return sitekey;
@ -29,8 +29,8 @@ export const sitekey = () => {
/** mCaptcha API routes */ /** mCaptcha API routes */
export const ROUTES = (() => { export const ROUTES = (() => {
const getConfig = '/api/v1/pow/config'; const getConfig = "/api/v1/pow/config";
const verififyPoW = '/api/v1/pow/verify'; const verififyPoW = "/api/v1/pow/verify";
return { return {
/** get URL to fetch PoW configuration */ /** get URL to fetch PoW configuration */
@ -41,24 +41,31 @@ export const ROUTES = (() => {
})(); })();
/** get mCaptcha verifify checkbox button */ /** get mCaptcha verifify checkbox button */
export const btn = () => { export const btn = (): HTMLInputElement => {
let btn; let btn;
return (() => { return (() => {
if (btn === null || btn === undefined) { if (btn === null || btn === undefined) {
btn = <HTMLInputElement>document.getElementById(btnId); btn = <HTMLInputElement>document.getElementById(btnId);
if (btn === null || btn === undefined) { if (btn === null || btn === undefined) {
throw new Error(`mCaptcha button not found)`); throw new Error("mCaptcha button not found)");
} }
} }
return btn; return btn;
})(); })();
}; };
export const messageText = () => { type messageTextReturn = {
const beforeID = 'widget__verification-text--before'; before: () => void;
const duringID = 'widget__verification-text--during'; after: () => void;
const errorID = 'widget__verification-text--error'; during: () => void;
const afterID = 'widget__verification-text--after'; error: () => void;
};
export const messageText = (): messageTextReturn => {
const beforeID = "widget__verification-text--before";
const duringID = "widget__verification-text--during";
const errorID = "widget__verification-text--error";
const afterID = "widget__verification-text--after";
const before = new LazyElement(beforeID); const before = new LazyElement(beforeID);
const after = new LazyElement(afterID); const after = new LazyElement(afterID);
@ -70,9 +77,9 @@ export const messageText = () => {
// let error: HTMLElement; // let error: HTMLElement;
/** runner fn to display HTMLElement **/ /** runner fn to display HTMLElement **/
const showMsg = (e: HTMLElement) => (e.style.display = 'block'); const showMsg = (e: HTMLElement) => (e.style.display = "block");
/** runner fn to hide HTMLElement **/ /** runner fn to hide HTMLElement **/
const hideMsg = (e: HTMLElement) => (e.style.display = 'none'); const hideMsg = (e: HTMLElement) => (e.style.display = "none");
return { return {
/** display "before" message **/ /** display "before" message **/
@ -109,4 +116,4 @@ export const messageText = () => {
}; };
}; };
export const inputId = 'mcaptcha-response'; export const inputId = "mcaptcha-response";

View File

@ -9,8 +9,8 @@
* MIT or <http://www.apache.org/licenses/LICENSE-2.0> for Apache. * MIT or <http://www.apache.org/licenses/LICENSE-2.0> for Apache.
*/ */
import genJsonPayload from './utils/genJsonPayload'; import genJsonPayload from "../../utils/genJsonPayload";
import * as CONST from './const'; import * as CONST from "./const";
type GetConfigPayload = { type GetConfigPayload = {
key: string; key: string;
@ -26,8 +26,7 @@ export type PoWConfig = {
* fetch proof-of-work configuration * fetch proof-of-work configuration
* @returns {PoWConfig} pow config * @returns {PoWConfig} pow config
* */ * */
export const fetchPoWConfig = async () => { export const fetchPoWConfig = async (): Promise<PoWConfig> => {
try {
const payload: GetConfigPayload = { const payload: GetConfigPayload = {
key: CONST.sitekey(), key: CONST.sitekey(),
}; };
@ -40,9 +39,6 @@ export const fetchPoWConfig = async () => {
const err = await res.json(); const err = await res.json();
throw new Error(err); throw new Error(err);
} }
} catch (err) {
throw err;
}
}; };
export default fetchPoWConfig; export default fetchPoWConfig;

View File

@ -9,26 +9,26 @@
* MIT or <http://www.apache.org/licenses/LICENSE-2.0> for Apache. * MIT or <http://www.apache.org/licenses/LICENSE-2.0> for Apache.
*/ */
import prove from './prove'; import prove from "./prove";
import fetchPoWConfig from './fetchPoWConfig'; import fetchPoWConfig from "./fetchPoWConfig";
import sendWork from './sendWork'; import sendWork from "./sendWork";
import sendToParent from './sendToParent'; import sendToParent from "./sendToParent";
import * as CONST from './const'; import * as CONST from "./const";
import '../main.scss'; import "../main.scss";
let LOCK = false; let LOCK = false;
/** add mcaptcha widget element to DOM */ /** add mcaptcha widget element to DOM */
export const registerVerificationEventHandler = () => { export const registerVerificationEventHandler = (): void => {
const verificationContainer = <HTMLElement>( const verificationContainer = <HTMLElement>(
document.querySelector('.widget__verification-container') document.querySelector(".widget__verification-container")
); );
verificationContainer.style.display = 'flex'; verificationContainer.style.display = "flex";
CONST.btn().addEventListener('click', e => solveCaptchaRunner(e)); CONST.btn().addEventListener("click", (e) => solveCaptchaRunner(e));
}; };
export const solveCaptchaRunner = async (e: Event) => { export const solveCaptchaRunner = async (e: Event): Promise<void> => {
if (LOCK) { if (LOCK) {
e.preventDefault(); e.preventDefault();
return; return;

View File

@ -9,9 +9,9 @@
* MIT or <http://www.apache.org/licenses/LICENSE-2.0> for Apache. * MIT or <http://www.apache.org/licenses/LICENSE-2.0> for Apache.
*/ */
import {gen_pow} from 'mcaptcha-browser'; import { gen_pow } from "mcaptcha-browser";
import {PoWConfig} from './fetchPoWConfig'; import { PoWConfig } from "./fetchPoWConfig";
import * as CONST from './const'; import * as CONST from "./const";
export type Work = { export type Work = {
result: string; result: string;
@ -30,12 +30,11 @@ type WasmWork = {
* @param {PoWConfig} config - the proof-of-work configuration using which * @param {PoWConfig} config - the proof-of-work configuration using which
* work needs to be computed * work needs to be computed
* */ * */
const prove = async (config: PoWConfig) => { const prove = async (config: PoWConfig): Promise<Work> => {
try {
const proofString = gen_pow( const proofString = gen_pow(
config.salt, config.salt,
config.string, config.string,
config.difficulty_factor, config.difficulty_factor
); );
const proof: WasmWork = JSON.parse(proofString); const proof: WasmWork = JSON.parse(proofString);
@ -47,9 +46,6 @@ const prove = async (config: PoWConfig) => {
}; };
return res; return res;
} catch (err) {
throw err;
}
}; };
export default prove; export default prove;

View File

@ -8,15 +8,15 @@
* this program. If not, see <https://spdx.org/licenses/MIT.html> for * this program. If not, see <https://spdx.org/licenses/MIT.html> for
* MIT or <http://www.apache.org/licenses/LICENSE-2.0> for Apache. * MIT or <http://www.apache.org/licenses/LICENSE-2.0> for Apache.
*/ */
import {Token} from './sendWork'; import {Token} from "./sendWork";
/** /**
* send pow validation token as message to parant of the iframe * send pow validation token as message to parant of the iframe
* @param {Token} token: token received from mCaptcha service * @param {Token} token: token received from mCaptcha service
* upon successful PoW validation * upon successful PoW validation
* */ * */
export const sendToParent = (token: Token) => { export const sendToParent = (token: Token): void => {
window.parent.postMessage(token, '*'); window.parent.postMessage(token, "*");
// TODO set origin. Make parent send origin as query parameter // TODO set origin. Make parent send origin as query parameter
// or as a message to iframe // or as a message to iframe
}; };

View File

@ -9,19 +9,19 @@
* MIT or <http://www.apache.org/licenses/LICENSE-2.0> for Apache. * MIT or <http://www.apache.org/licenses/LICENSE-2.0> for Apache.
*/ */
import genJsonPayload from './utils/genJsonPayload'; import genJsonPayload from "../../utils/genJsonPayload";
import * as CONST from './const'; import * as CONST from "./const";
import {Work} from './prove'; import {Work} from "./prove";
export type Token = { export type Token = {
token: string; token: string;
}; };
export const sendWork = async (payload: Work) => { export const sendWork = async (payload: Work): Promise<Token> => {
try { try {
const res = await fetch(CONST.ROUTES.verififyPoW, genJsonPayload(payload)); const res = await fetch(CONST.ROUTES.verififyPoW, genJsonPayload(payload));
if (res.ok) { if (res.ok) {
console.debug('work verified'); console.debug("work verified");
const token: Token = await res.json(); const token: Token = await res.json();
console.debug(`token ${token.token}`); console.debug(`token ${token.token}`);
return token; return token;

View File

@ -8,13 +8,13 @@
* this program. If not, see <https://spdx.org/licenses/MIT.html> for * this program. If not, see <https://spdx.org/licenses/MIT.html> for
* MIT or <http://www.apache.org/licenses/LICENSE-2.0> for Apache. * MIT or <http://www.apache.org/licenses/LICENSE-2.0> for Apache.
*/ */
import * as CONST from '../const'; import * as CONST from "../const";
import {getBaseHtml, sitekey, checkbox} from './setupTests'; import {getBaseHtml, sitekey, checkbox} from "./setupTests";
import * as TESTElements from './setupTests'; import * as TESTElements from "./setupTests";
it('const works', () => { it("const works", () => {
const body = document.querySelector('body'); const body = document.querySelector("body");
const container = getBaseHtml(); const container = getBaseHtml();
body.appendChild(container); body.appendChild(container);
expect(CONST.sitekey()).toBe(sitekey); expect(CONST.sitekey()).toBe(sitekey);
@ -22,29 +22,29 @@ it('const works', () => {
// display after // display after
CONST.messageText().after(); CONST.messageText().after();
expect(TESTElements.afterMsg.style.display).toBe('block'); expect(TESTElements.afterMsg.style.display).toBe("block");
expect(TESTElements.beforeMsg.style.display).toBe('none'); expect(TESTElements.beforeMsg.style.display).toBe("none");
expect(TESTElements.duringMsg.style.display).toBe('none'); expect(TESTElements.duringMsg.style.display).toBe("none");
expect(TESTElements.errorMsg.style.display).toBe('none'); expect(TESTElements.errorMsg.style.display).toBe("none");
// display before // display before
CONST.messageText().before(); CONST.messageText().before();
expect(TESTElements.afterMsg.style.display).toBe('none'); expect(TESTElements.afterMsg.style.display).toBe("none");
expect(TESTElements.beforeMsg.style.display).toBe('block'); expect(TESTElements.beforeMsg.style.display).toBe("block");
expect(TESTElements.duringMsg.style.display).toBe('none'); expect(TESTElements.duringMsg.style.display).toBe("none");
expect(TESTElements.errorMsg.style.display).toBe('none'); expect(TESTElements.errorMsg.style.display).toBe("none");
// display during // display during
CONST.messageText().during(); CONST.messageText().during();
expect(TESTElements.afterMsg.style.display).toBe('none'); expect(TESTElements.afterMsg.style.display).toBe("none");
expect(TESTElements.beforeMsg.style.display).toBe('none'); expect(TESTElements.beforeMsg.style.display).toBe("none");
expect(TESTElements.duringMsg.style.display).toBe('block'); expect(TESTElements.duringMsg.style.display).toBe("block");
expect(TESTElements.errorMsg.style.display).toBe('none'); expect(TESTElements.errorMsg.style.display).toBe("none");
// display error // display error
CONST.messageText().error(); CONST.messageText().error();
expect(TESTElements.afterMsg.style.display).toBe('none'); expect(TESTElements.afterMsg.style.display).toBe("none");
expect(TESTElements.beforeMsg.style.display).toBe('none'); expect(TESTElements.beforeMsg.style.display).toBe("none");
expect(TESTElements.duringMsg.style.display).toBe('none'); expect(TESTElements.duringMsg.style.display).toBe("none");
expect(TESTElements.errorMsg.style.display).toBe('block'); expect(TESTElements.errorMsg.style.display).toBe("block");
}); });

View File

@ -8,28 +8,28 @@
* this program. If not, see <https://spdx.org/licenses/MIT.html> for * this program. If not, see <https://spdx.org/licenses/MIT.html> for
* MIT or <http://www.apache.org/licenses/LICENSE-2.0> for Apache. * MIT or <http://www.apache.org/licenses/LICENSE-2.0> for Apache.
*/ */
import * as CONST from '../const'; import * as CONST from "../const";
export const sitekey = 'imbatman'; export const sitekey = "imbatman";
export const checkbox = <HTMLInputElement>document.createElement('input'); export const checkbox = <HTMLInputElement>document.createElement("input");
checkbox.type = 'checkbox'; checkbox.type = "checkbox";
checkbox.id = CONST.btnId; checkbox.id = CONST.btnId;
const getMessages = (state: string) => { const getMessages = (state: string) => {
const msg = <HTMLElement>document.createElement('span'); const msg = <HTMLElement>document.createElement("span");
msg.id = `widget__verification-text--${state}`; msg.id = `widget__verification-text--${state}`;
return msg; return msg;
}; };
export const beforeMsg = getMessages('before'); export const beforeMsg = getMessages("before");
export const afterMsg = getMessages('after'); export const afterMsg = getMessages("after");
export const duringMsg = getMessages('during'); export const duringMsg = getMessages("during");
export const errorMsg = getMessages('error'); export const errorMsg = getMessages("error");
/** get base HTML with empty mCaptcha container */ /** get base HTML with empty mCaptcha container */
export const getBaseHtml = () => { export const getBaseHtml = (): HTMLFormElement => {
const form = <HTMLFormElement>document.createElement('form'); const form = <HTMLFormElement>document.createElement("form");
form.appendChild(checkbox); form.appendChild(checkbox);
form.appendChild(beforeMsg); form.appendChild(beforeMsg);
form.appendChild(duringMsg); form.appendChild(duringMsg);

View File

@ -1,30 +0,0 @@
/*
* mCaptcha is a PoW based DoS protection software.
* This is the frontend web component of the mCaptcha system
* Copyright © 2021 Aravinth Manivnanan <realaravinth@batsense.net>.
*
* Use of this source code is governed by Apache 2.0 or MIT license.
* You shoud have received a copy of MIT and Apache 2.0 along with
* this program. If not, see <https://spdx.org/licenses/MIT.html> for
* MIT or <http://www.apache.org/licenses/LICENSE-2.0> for Apache.
*/
import genJsonPayload from './genJsonPayload';
'use strict';
const payload = {
username: 'Jhon',
};
const value = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
};
it('getFromUrl workds', () => {
expect(genJsonPayload(payload)).toEqual(value);
});

View File

@ -1,23 +0,0 @@
/*
* mCaptcha is a PoW based DoS protection software.
* This is the frontend web component of the mCaptcha system
* Copyright © 2021 Aravinth Manivnanan <realaravinth@batsense.net>.
*
* Use of this source code is governed by Apache 2.0 or MIT license.
* You shoud have received a copy of MIT and Apache 2.0 along with
* this program. If not, see <https://spdx.org/licenses/MIT.html> for
* MIT or <http://www.apache.org/licenses/LICENSE-2.0> for Apache.
*/
const genJsonPayload = (payload: any) => {
const value = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
};
return value;
};
export default genJsonPayload;

2234
yarn.lock

File diff suppressed because it is too large Load Diff