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
# run: make frontend
#
- name: lint frontend
run: yarn lint
- name: run tests
run: make test

View File

@ -4,6 +4,7 @@
"version": "1.0.0",
"scripts": {
"build": "webpack --mode production",
"lint": "yarn run eslint templates",
"start": "webpack-dev-server --mode development --progress --color",
"test": "jest"
},
@ -12,10 +13,13 @@
"@types/jsdom": "^16.2.10",
"@types/node": "^15.0.2",
"@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",
"css-loader": "^5.2.4",
"css-minimizer-webpack-plugin": "^2.0.0",
"dart-sass": "^1.25.0",
"eslint": "^7.32.0",
"jest": "^26.6.3",
"jest-fetch-mock": "^3.0.3",
"jsdom": "^16.5.3",

View File

@ -17,7 +17,7 @@
use actix_identity::Identity;
use actix_web::{web, HttpResponse, Responder};
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 serde::{Deserialize, Serialize};
@ -190,8 +190,16 @@ async fn update_levels(
}
try_join_all(futs).await?;
if let Err(ServiceError::CaptchaError(e)) = data.captcha.remove(RemoveCaptcha(payload.key.clone())).await {
log::error!("Deleting captcha key {} while updating it, error: {:?}", &payload.key, e)
if let Err(ServiceError::CaptchaError(e)) = data
.captcha
.remove(RemoveCaptcha(payload.key.clone()))
.await
{
log::error!(
"Deleting captcha key {} while updating it, error: {:?}",
&payload.key,
e
)
}
Ok(HttpResponse::Ok())
}

View File

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

View File

@ -14,6 +14,6 @@
* 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/>.
*/
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
* 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 genJsonPayload from '../../../utils/genJsonPayload';
import getFormUrl from '../../../utils/getFormUrl';
import registerShowPassword from '../../../components/showPassword';
import createError from '../../../components/error/index';
import isBlankString from "../../../utils/isBlankString";
import genJsonPayload from "../../../utils/genJsonPayload";
import getFormUrl from "../../../utils/getFormUrl";
import registerShowPassword from "../../../components/showPassword";
import createError from "../../../components/error/index";
//import '../forms.scss';
export const getPassword = () => {
const passwordElement = <HTMLInputElement>document.getElementById('password');
export const getPassword = (): string | null => {
const passwordElement = <HTMLInputElement>document.getElementById("password");
if (passwordElement === null) {
console.debug('Password is null');
console.debug("Password is null");
return;
}
return passwordElement.value;
};
const login = async (e: Event) => {
const login = async (e: Event): Promise<void> => {
e.preventDefault();
const loginElement = <HTMLInputElement>document.getElementById('login');
const loginElement = <HTMLInputElement>document.getElementById("login");
if (loginElement === null) {
console.debug('login element element is null');
console.debug("login element element is null");
return;
}
const login = loginElement.value;
isBlankString(login, 'username', e);
isBlankString(login, "username", e);
const password = getPassword();
@ -65,9 +65,9 @@ const login = async (e: Event) => {
}
};
export const index = () => {
const form = <HTMLFontElement>document.getElementById('form');
form.addEventListener('submit', login, true);
export const index = (): void => {
const form = <HTMLFontElement>document.getElementById("form");
form.addEventListener("submit", login, true);
registerShowPassword();
init();
};

View File

@ -15,13 +15,13 @@
* 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();
mockAlert();
@ -30,14 +30,14 @@ beforeEach(() => {
fetchMock.resetMocks();
});
it('finds exchange', async () => {
it("finds exchange", async () => {
fetchMock.mockResponseOnce(JSON.stringify({exists: true}));
document.body.innerHTML = getRegistrationFormHtml();
document.querySelector('body').appendChild(setup());
document.querySelector("body").appendChild(setup());
const emailField = <HTMLInputElement>document.getElementById('email');
emailField.setAttribute('value', 'test@a.com');
const emailField = <HTMLInputElement>document.getElementById("email");
emailField.setAttribute("value", "test@a.com");
expect(await emailExists()).toBe(true);

View File

@ -15,15 +15,15 @@
* 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 createError from '../../../components/error/index';
import genJsonPayload from "../../../utils/genJsonPayload";
import createError from "../../../components/error/index";
const emailExists = async (element?: HTMLInputElement) => {
const emailExists = async (element?: HTMLInputElement): Promise<boolean> => {
let email;
if (element === undefined || element === null) {
email = <HTMLInputElement>document.getElementById('email');
email = <HTMLInputElement>document.getElementById("email");
} else {
email = element;
}
@ -37,7 +37,7 @@ const emailExists = async (element?: HTMLInputElement) => {
if (res.ok) {
const data = await res.json();
if (data.exists) {
email.className += ' form__in-field--warn';
email.className += " form__in-field--warn";
createError(`Email "${val}" is already used`);
return data.exists;
}

View File

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

View File

@ -14,13 +14,13 @@
* 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/>.
*/
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();
mockAlert();
@ -29,16 +29,16 @@ beforeEach(() => {
fetchMock.resetMocks();
});
it('finds exchange', async () => {
it("finds exchange", async () => {
fetchMock.mockResponseOnce(JSON.stringify({exists: true}));
document.body.innerHTML = getLoginFormHtml();
document.querySelector('body').appendChild(setup());
const usernameField = <HTMLInputElement>document.querySelector('#username');
usernameField.value = 'test';
document.querySelector("body").appendChild(setup());
const usernameField = <HTMLInputElement>document.querySelector("#username");
usernameField.value = "test";
expect(await userExists()).toBe(true);
usernameField.value = 'test';
usernameField.value = "test";
fetchMock.mockResponseOnce(JSON.stringify({exists: 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/>.
*/
import ROUTES from '../../../api/v1/routes';
import ROUTES from "../../../api/v1/routes";
import genJsonPayload from '../../../utils/genJsonPayload';
import createError from '../../../components/error/index';
import genJsonPayload from "../../../utils/genJsonPayload";
import createError from "../../../components/error/index";
const userExists = async (element?: HTMLInputElement) => {
const userExists = async (element?: HTMLInputElement): Promise<boolean> => {
console.log(element);
let username;
if (element === undefined) {
username = <HTMLInputElement>document.getElementById('username');
username = <HTMLInputElement>document.getElementById("username");
} else {
username = element;
}
@ -37,7 +37,7 @@ const userExists = async (element?: HTMLInputElement) => {
if (res.ok) {
const data = await res.json();
if (data.exists) {
username.className += ' form__in-field--warn';
username.className += " form__in-field--warn";
createError(`Username "${val}" taken`);
}
return data.exists;

View File

@ -15,17 +15,17 @@
* 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 {
form.get();
} catch (e) {
expect(e.message).toBe('Element form is undefined');
expect(e.message).toBe("Element form is undefined");
}
const element = document.createElement('form');
element.id = 'form';
const element = document.createElement("form");
element.id = "form";
document.body.appendChild(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
* 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);
export default FORM;

View File

@ -15,9 +15,9 @@
* 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 {
additionalData();
} catch (e) {
@ -26,8 +26,8 @@ it('sudo form works', () => {
);
}
const element = document.createElement('div');
element.id = 'additional-data';
const element = document.createElement("div");
element.id = "additional-data";
document.body.appendChild(element);
expect(additionalData()).toBe(element);
});

View File

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

View File

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

View File

@ -15,16 +15,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import createError from './index';
import * as e from './index';
import createError from "./index";
import * as e from "./index";
import setup from './setUpTests';
import setup from "./setUpTests";
'use strict';
"use strict";
jest.useFakeTimers();
it('checks if error boxes work', () => {
it("checks if error boxes work", () => {
document.body.append(setup());
const getMsg = (num: number) => `message ${num}`;
@ -32,21 +32,21 @@ it('checks if error boxes work', () => {
let msg = document.querySelector(`.${e.ERR_MSG_CONTAINER}`);
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();
msg = document.querySelector(`.${e.ERR_MSG_CONTAINER}`);
expect(msg).toEqual(null);
const errElement = document.createElement('p');
const errElement = document.createElement("p");
errElement.appendChild(document.createTextNode(getMsg(2)));
createError(errElement);
msg = document.querySelector(`.${e.ERR_MSG_CONTAINER}`).querySelector('p');
msg = document.querySelector(`.${e.ERR_MSG_CONTAINER}`).querySelector("p");
expect(msg).toEqual(errElement);
let timeOutElement = document.createElement('p');
const timeOutElement = document.createElement("p");
timeOutElement.appendChild(document.createTextNode(getMsg(2)));
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);
jest.runOnlyPendingTimers();
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/>.
*/
export const ERR_CONTAINER_ID = 'err__container';
export const ERR_MSG_CONTAINER = 'err__msg-container'; // class
export const ERR_CLOSE = 'err__close'; // class
export const ERR_CONTAINER_ID = "err__container";
export const ERR_MSG_CONTAINER = "err__msg-container"; // class
export const ERR_CLOSE = "err__close"; // class
export const DEFAULT_LIFETIME = 5000;
@ -41,11 +41,11 @@ const err = () => {
const createError = (
message: string | HTMLElement,
lifetime: number = DEFAULT_LIFETIME,
) => {
const box = document.createElement('div');
): void => {
const box = document.createElement("div");
const msg = () => {
if (typeof message === 'string') {
if (typeof message === "string") {
return document.createTextNode(message);
} else {
return message;
@ -55,8 +55,8 @@ const createError = (
box.className = ERR_MSG_CONTAINER;
box.appendChild(msg());
const deleteBtn = document.createElement('button');
const deleteMsg = document.createTextNode('x');
const deleteBtn = document.createElement("button");
const deleteMsg = document.createTextNode("x");
deleteBtn.appendChild(deleteMsg);
deleteBtn.className = ERR_CLOSE;
box.appendChild(deleteBtn);
@ -71,7 +71,7 @@ const createError = (
box.remove();
};
deleteBtn.addEventListener('click', e => deleteHandler(e));
deleteBtn.addEventListener("click", e => deleteHandler(e));
};
export default createError;

View File

@ -14,10 +14,10 @@
* 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/>.
*/
import * as e from './index';
import * as e from "./index";
const setup = () => {
let x = document.createElement('div');
const setup = (): HTMLElement => {
const x = document.createElement("div");
x.id = e.ERR_CONTAINER_ID;
return x;
};

View File

@ -15,12 +15,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
const showPasswordButtonClassHidden = 'show-password--hide';
const showPasswordButtonClassShowing = 'show-password--show';
const showPasswordButtonClassHidden = "show-password--hide";
const showPasswordButtonClassShowing = "show-password--show";
const container = 'show-password-container';
const container = "show-password-container";
let display = 'hidden';
let display = "hidden";
const showPasswordButtons = () => {
let buttons: NodeListOf<HTMLElement>;
@ -49,45 +49,45 @@ const hidePasswordButtons = () => {
};
// e is click event from show password container
export const showPassword = () => {
const inputs = document.body.querySelectorAll('input');
export const showPassword = (): void => {
const inputs = document.body.querySelectorAll("input");
if (display == 'hidden') {
display = 'show';
if (display == "hidden") {
display = "show";
inputs.forEach(element => {
if (element.type === 'password') {
element.type = 'text';
if (element.type === "password") {
element.type = "text";
}
});
showPasswordButtons().forEach((button: HTMLInputElement) => {
button.style.display = 'none';
button.style.display = "none";
});
hidePasswordButtons().forEach((button: HTMLInputElement) => {
button.style.display = 'inline';
button.style.display = "inline";
});
} else {
display = 'hidden';
display = "hidden";
inputs.forEach(element => {
if (element.type === 'text' && element.name.includes('password')) {
element.type = 'password';
if (element.type === "text" && element.name.includes("password")) {
element.type = "password";
}
});
showPasswordButtons().forEach((button: HTMLInputElement) => {
button.style.display = 'inline';
button.style.display = "inline";
});
hidePasswordButtons().forEach((button: HTMLInputElement) => {
button.style.display = 'none';
button.style.display = "none";
});
}
// posibily clicked on something else
};
export const registerShowPassword = () => {
export const registerShowPassword = (): void => {
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/>.
*/
import registerShowPassword from './index';
import {showPassword} from './index';
import registerShowPassword from "./index";
import {showPassword} from "./index";
const initial_content = `
<form class="sitekey-form" method="POST" action="/api/v1/signin" id="form" data-bitwarden-watching="1">
@ -41,49 +41,49 @@ const initial_content = `
</form>
`;
it('show password works', () => {
it("show password works", () => {
document.body.innerHTML = initial_content;
const container = <HTMLElement>(
document.querySelector(`.show-password-container`)
document.querySelector(".show-password-container")
);
const hide = <HTMLElement>container.querySelector('.show-password--hide');
const show = <HTMLElement>container.querySelector('.show-password--show');
const password = <HTMLInputElement>document.getElementById('password');
show.style.display = 'inline';
hide.style.display = 'none';
const hide = <HTMLElement>container.querySelector(".show-password--hide");
const show = <HTMLElement>container.querySelector(".show-password--show");
const password = <HTMLInputElement>document.getElementById("password");
show.style.display = "inline";
hide.style.display = "none";
showPassword();
expect(hide.style.display).toEqual('inline');
expect(show.style.display).toEqual('none');
expect(password.type).toEqual('text');
expect(hide.style.display).toEqual("inline");
expect(show.style.display).toEqual("none");
expect(password.type).toEqual("text");
showPassword();
expect(show.style.display).toEqual('inline');
expect(hide.style.display).toEqual('none');
expect(password.type).toEqual('password');
expect(show.style.display).toEqual("inline");
expect(hide.style.display).toEqual("none");
expect(password.type).toEqual("password");
});
it('show password click works', () => {
it("show password click works", () => {
document.body.innerHTML = initial_content;
const container = <HTMLElement>(
document.querySelector(`.show-password-container`)
document.querySelector(".show-password-container")
);
const hide = <HTMLElement>container.querySelector('.show-password--hide');
const show = <HTMLElement>container.querySelector('.show-password--show');
const password = <HTMLInputElement>document.getElementById('password');
show.style.display = 'inline';
hide.style.display = 'none';
const hide = <HTMLElement>container.querySelector(".show-password--hide");
const show = <HTMLElement>container.querySelector(".show-password--show");
const password = <HTMLInputElement>document.getElementById("password");
show.style.display = "inline";
hide.style.display = "none";
registerShowPassword();
container.click();
expect(hide.style.display).toEqual('inline');
expect(show.style.display).toEqual('none');
expect(password.type).toEqual('text');
expect(hide.style.display).toEqual("inline");
expect(show.style.display).toEqual("none");
expect(password.type).toEqual("text");
container.click();
expect(show.style.display).toEqual('inline');
expect(hide.style.display).toEqual('none');
expect(password.type).toEqual('password');
expect(show.style.display).toEqual("inline");
expect(hide.style.display).toEqual("none");
expect(password.type).toEqual("password");
});

View File

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

View File

@ -15,13 +15,13 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import './mobile.scss';
import './auth/css/mobile.scss';
import './components/details-footer/mobile.scss';
import './panel/css/mobile.scss';
import './panel/settings/mobile.scss';
import './panel/header/taskbar/mobile.scss';
import './panel/navbar/mobile.scss';
import './panel/help-banner/mobile.scss';
import './panel/sitekey/add/css/mobile.scss';
import './panel/sitekey/list/css/mobile.scss';
import "./mobile.scss";
import "./auth/css/mobile.scss";
import "./components/details-footer/mobile.scss";
import "./panel/css/mobile.scss";
import "./panel/settings/mobile.scss";
import "./panel/header/taskbar/mobile.scss";
import "./panel/navbar/mobile.scss";
import "./panel/help-banner/mobile.scss";
import "./panel/sitekey/add/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
* 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/>.
*/
import genJsonPayload from '../../../utils/genJsonPayload';
import createError from '../../../components/error';
import genJsonPayload from "../../../utils/genJsonPayload";
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 TABLE_BODY = document.querySelector('.notification__body');
const BTN = document.querySelectorAll(".notification__mark-read-btn");
const TABLE_BODY = document.querySelector(".notification__body");
const notification_record = (id: number) =>
<HTMLElement>TABLE_BODY.querySelector(`#notification__item-${id}`);
@ -46,10 +46,10 @@ const markRead = async (e: Event) => {
const addMarkReadEventListenet = () => {
BTN.forEach(btn => {
btn.addEventListener('click', markRead, true);
btn.addEventListener("click", markRead, true);
});
};
export const index = () => {
export const index = (): void => {
addMarkReadEventListenet();
};

View File

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

View File

@ -15,28 +15,28 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import registerShowPassword from '../../components/showPassword/';
import CopyIcon from '../../components/clipboard/';
import createError from '../../components/error/';
import registerShowPassword from "../../components/showPassword/";
import CopyIcon from "../../components/clipboard/";
import createError from "../../components/error/";
import emailExists from '../../auth/register/ts/emailExists';
import userExists from '../../auth/register/ts/userExists';
import emailExists from "../../auth/register/ts/emailExists";
import userExists from "../../auth/register/ts/userExists";
import LazyElement from '../../utils/lazyElement';
import isBlankString from '../../utils/isBlankString';
import getFormUrl from '../../utils/getFormUrl';
import genJsonPayload from '../../utils/genJsonPayload';
import LazyElement from "../../utils/lazyElement";
import isBlankString from "../../utils/isBlankString";
import getFormUrl from "../../utils/getFormUrl";
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_DONE_ICON = 'settings__secret-copy-done';
const SECRET_COPY_ICON = "settings__secret-copy";
const SECRET_COPY_DONE_ICON = "settings__secret-copy-done";
// form IDs
const DELETE_FORM = 'settings__delete-form';
const EMAIL_FORM = 'settings__email-form';
const USERNAME_FORM = 'settings__username-form';
const SECRET_FORM = 'settings__secret-form';
const DELETE_FORM = "settings__delete-form";
const EMAIL_FORM = "settings__email-form";
const USERNAME_FORM = "settings__username-form";
const SECRET_FORM = "settings__secret-form";
// form elements
const deleteForm = new LazyElement(DELETE_FORM);
@ -45,8 +45,8 @@ const usernameForm = new LazyElement(USERNAME_FORM);
const secretForm = new LazyElement(SECRET_FORM);
// field IDs
const EMAIL = 'email';
const USERNAME = 'username';
const EMAIL = "email";
const USERNAME = "username";
// field elements
const emailField = new LazyElement(EMAIL);
@ -57,7 +57,7 @@ const updateEmail = async (e: Event) => {
e.preventDefault();
const emailElement = <HTMLInputElement>emailField.get();
const email = emailElement.value;
isBlankString(email, 'email', e);
isBlankString(email, "email", e);
if (await emailExists(emailElement)) {
return;
} else {
@ -80,7 +80,7 @@ const updateUsername = async (e: Event) => {
e.preventDefault();
const usernameElement = <HTMLInputElement>usernameField.get();
const username = usernameElement.value;
isBlankString(username, 'username', e);
isBlankString(username, "username", e);
if (await userExists(usernameElement)) {
return;
} else {
@ -101,7 +101,7 @@ const updateUsername = async (e: Event) => {
const updateSecret = (e: Event) => {
e.preventDefault();
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)) {
window.location.assign(VIEWS.updateSecret);
}
@ -118,14 +118,14 @@ const deleteAccount = (e: Event) => {
// regist form event handlers
const registerForms = () => {
deleteForm.get().addEventListener('submit', e => deleteAccount(e), true);
emailForm.get().addEventListener('submit', e => updateEmail(e), true);
usernameForm.get().addEventListener('submit', e => updateUsername(e), true);
deleteForm.get().addEventListener("submit", (e) => deleteAccount(e), true);
emailForm.get().addEventListener("submit", (e) => updateEmail(e), true);
usernameForm.get().addEventListener("submit", (e) => updateUsername(e), true);
console.log(usernameField.get());
usernameField
.get()
.addEventListener('input', async () => await userExists(), false);
secretForm.get().addEventListener('submit', e => updateSecret(e), true);
.addEventListener("input", async () => await userExists(), false);
secretForm.get().addEventListener("submit", (e) => updateSecret(e), true);
};
// 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
const index = () => {
const index = (): void => {
registerShowPassword();
initCopySecret();
registerForms();

View File

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

View File

@ -15,16 +15,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import getNumLevels from './levels/getNumLevels';
import {getAddForm, trim, addLevel} from './setupTests';
import setup from '../../../../components/error/setUpTests';
import getNumLevels from "./levels/getNumLevels";
import {getAddForm, trim, addLevel} from "./setupTests";
import setup from "../../../../components/error/setUpTests";
document.body.innerHTML = getAddForm();
document.body.appendChild(setup());
jest.useFakeTimers();
it('addLevelButton works', () => {
it("addLevelButton works", () => {
expect(getNumLevels()).toBe(1);
// add a level
addLevel(2, 4);
@ -36,7 +36,7 @@ it('addLevelButton works', () => {
addLevel(4, 9);
expect(getNumLevels()).toBe(3);
let a = document.body.innerHTML;
const a = document.body.innerHTML;
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
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import validateLevel from './levels/validateLevel';
import getNumLevels from './levels/getNumLevels';
import * as UpdateLevel from './levels/updateLevel';
import validateLevel from "./levels/validateLevel";
import getNumLevels from "./levels/getNumLevels";
import * as UpdateLevel from "./levels/updateLevel";
import {
getRemoveButtonHTML,
addRemoveLevelButtonEventListener,
} from './removeLevelButton';
import CONST from './const';
} from "./removeLevelButton";
import CONST from "./const";
import log from '../../../../logger';
import log from "../../../../logger";
/**
* Gets executed when 'Add' Button is clicked to add levels
@ -39,30 +39,30 @@ const addLevel = (e: Event) => {
const isValid = validateLevel(onScreenLevel);
log.debug(`[addLevelButton] isValid: ${isValid}`);
if (!isValid) {
let error = `Aborting level ${onScreenLevel} addition`;
const error = `Aborting level ${onScreenLevel} addition`;
return log.error(error);
}
FIELDSET.replaceChild(getRemoveButtonHTML(onScreenLevel), PARENT);
const newLevelElement = getHtml(onScreenLevel + 1);
FIELDSET.insertAdjacentElement('afterend', newLevelElement);
FIELDSET.insertAdjacentElement("afterend", newLevelElement);
UpdateLevel.register(onScreenLevel);
addRemoveLevelButtonEventListener(onScreenLevel);
addLevelButtonAddEventListener();
const main = document.querySelector('body');
const main = document.querySelector("body");
const style = main.style.display;
main.style.display = 'none';
main.style.display = "none";
main.style.display = style;
};
/** adds onclick event listener */
const addLevelButtonAddEventListener = () => {
const addLevelButtonAddEventListener = (): void => {
const addLevelButton = <HTMLElement>(
document.querySelector(`.${CONST.ADD_LEVEL_BUTTON}`)
);
addLevelButton.addEventListener('click', addLevel);
addLevelButton.addEventListener("click", addLevel);
};
/**
@ -72,25 +72,25 @@ const addLevelButtonAddEventListener = () => {
const getHtml = (level: number) => {
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.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;
const legendText = document.createTextNode(`Level ${level}`);
legend.appendChild(legendText);
fieldset.appendChild(legend);
const vistitorLabel = document.createElement('label'); //document.createElement('label');
const vistitorLabel = document.createElement("label"); //document.createElement('label');
vistitorLabel.className = CONST.LABEL_CLASS;
const visitorText = document.createTextNode('Visitor');
const visitorText = document.createTextNode("Visitor");
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}`;
visitor.className = CONST.LEVEL_INPUT_CLASS;
visitor.type = 'number';
visitor.type = "number";
visitor.name = visitorId;
visitor.id = visitorId;
vistitorLabel.htmlFor = visitorId;
@ -98,13 +98,13 @@ const getHtml = (level: number) => {
fieldset.appendChild(vistitorLabel);
const difficultyLabel = document.createElement('label');
const difficultyLabel = document.createElement("label");
difficultyLabel.className = CONST.LABEL_CLASS;
const difficultyText = document.createTextNode('Difficulty');
const difficultyText = document.createTextNode("Difficulty");
difficultyLabel.appendChild(difficultyText);
const difficulty = document.createElement('input');
const difficulty = document.createElement("input");
const difficultyID = `${CONST.DIFFICULTY_WITHOUT_LEVEL}${level}`;
difficulty.type = 'number';
difficulty.type = "number";
difficulty.name = difficultyID;
difficulty.className = CONST.LEVEL_INPUT_CLASS;
difficulty.id = difficultyID;
@ -113,18 +113,18 @@ const getHtml = (level: number) => {
fieldset.appendChild(difficultyLabel);
const addLevelLabel = document.createElement('label');
const addLevelLabel = document.createElement("label");
addLevelLabel.className = CONST.REMOVE_LEVEL_LABEL_CLASS;
const addLevel = document.createElement('input');
const addLevel = document.createElement("input");
addLevel.className = CONST.ADD_LEVEL_BUTTON;
addLevel.type = 'button';
const addLevelButtonID = 'add';
addLevel.type = "button";
const addLevelButtonID = "add";
addLevel.name = addLevelButtonID;
addLevel.id = addLevelButtonID;
addLevelLabel.htmlFor = addLevelButtonID;
const addText = document.createTextNode('Add level');
const addText = document.createTextNode("Add level");
addLevelLabel.appendChild(addText);
addLevel.value = 'Add';
addLevel.value = "Add";
addLevelLabel.appendChild(addLevel);
fieldset.appendChild(addLevelLabel);

View File

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

View File

@ -15,30 +15,31 @@
* 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 genJsonPayload from '../../../../../utils/genJsonPayload';
import getFormUrl from "../../../../../utils/getFormUrl";
import genJsonPayload from "../../../../../utils/genJsonPayload";
import VIEWS from '../../../../../views/v1/routes';
import VIEWS from "../../../../../views/v1/routes";
import validateDescription from './validateDescription';
import validateDuration from './validateDuration';
import validateDescription from "./validateDescription";
import validateDuration from "./validateDuration";
import createError from '../../../../../components/error';
import createError from "../../../../../components/error";
export const SITE_KEY_FORM_CLASS = 'sitekey-form';
export const FORM = <HTMLFormElement>document.querySelector(`.${SITE_KEY_FORM_CLASS}`);
export const SITE_KEY_FORM_CLASS = "sitekey-form";
export const FORM = <HTMLFormElement>(
document.querySelector(`.${SITE_KEY_FORM_CLASS}`)
);
export const addSubmitEventListener = () => {
FORM.addEventListener('submit', submit, true);
};
export const addSubmitEventListener = (): void =>
FORM.addEventListener("submit", submit, true);
const submit = async (e: Event) => {
e.preventDefault();
const description = validateDescription(e);
const duration = validateDuration(e);
const duration = validateDuration();
const formUrl = getFormUrl(FORM);

View File

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

View File

@ -15,12 +15,12 @@
* 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 inputElement = <HTMLInputElement>document.getElementById('description');
const validateDescription = (e: Event): string => {
const inputElement = <HTMLInputElement>document.getElementById("description");
const val = inputElement.value;
const filed = 'Description';
const filed = "Description";
isBlankString(val, filed, e);
return val;
};

View File

@ -14,8 +14,6 @@
* 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/>.
*/
import isNumber from '../../../../../utils/isNumber';
//const validateDuration = (e: Event) => {
// const duartionElement = <HTMLInputElement>document.getElementById('duration');
// const duration = parseInt(duartionElement.value);
@ -31,30 +29,28 @@ import isNumber from '../../../../../utils/isNumber';
//
//export default validateDuration;
import validateDuration from './validateDuration';
import {getAddForm, fillDuration} from '../setupTests';
import validateDuration from "./validateDuration";
import {getAddForm, fillDuration} from "../setupTests";
document.body.innerHTML = getAddForm();
const emptyErr = "can't be empty";
const NaNErr = 'duration can contain nubers only';
const zeroErr = 'duration must be greater than zero';
const NaNErr = "duration can contain nubers only";
const zeroErr = "duration must be greater than zero";
const duration = 30;
it('validateDuration workds', () => {
it("validateDuration workds", () => {
try {
const event = new Event('submit');
validateDuration(event);
validateDuration();
} catch (e) {
expect(e.message).toContain(emptyErr);
}
// fill string error
try {
fillDuration('testing');
const event = new Event('submit');
validateDuration(event);
fillDuration("testing");
validateDuration();
} catch (e) {
expect(e.message).toContain(NaNErr);
}
@ -62,13 +58,11 @@ it('validateDuration workds', () => {
// zero err
try {
fillDuration(0);
const event = new Event('submit');
validateDuration(event);
validateDuration();
} catch (e) {
expect(e.message).toContain(zeroErr);
}
fillDuration(duration);
const event = new Event('submit');
expect(validateDuration(event)).toBe(duration);
expect(validateDuration()).toBe(duration);
});

View File

@ -14,17 +14,17 @@
* 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/>.
*/
import isNumber from '../../../../../utils/isNumber';
import isNumber from "../../../../../utils/isNumber";
const validateDuration = (e: Event) => {
const duartionElement = <HTMLInputElement>document.getElementById('duration');
const validateDuration = (): number => {
const duartionElement = <HTMLInputElement>document.getElementById("duration");
const duration = parseInt(duartionElement.value);
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) {
throw new Error('duration must be greater than zero');
throw new Error("duration must be greater than zero");
}
return duration;
};

View File

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

View File

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

View File

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

View File

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

View File

@ -15,16 +15,16 @@
* 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 */
const getNumLevels = () => {
const getNumLevels = (): number => {
let numLevels = 0;
document
.querySelectorAll(`.${CONST.LEVEL_CONTAINER_CLASS}`)
.forEach(_ => numLevels++);
.forEach(() => numLevels++);
log.debug(`[getNumLevels]: numLevels: ${numLevels}`);
return numLevels;
};

View File

@ -15,7 +15,7 @@
* 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 */
export type Level = {
@ -95,7 +95,7 @@ export const LEVELS = (function() {
}
}
levels.levels = tmpLevel.levels;
log.debug(`post update:`);
log.debug("post update:");
LEVELS.print();
return true;
} catch (e) {
@ -133,7 +133,7 @@ export const LEVELS = (function() {
}
}
levels.levels = tmpLevel.levels;
log.debug('Post remove:');
log.debug("Post remove:");
LEVELS.print();
return true;
} catch (e) {

View File

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

View File

@ -15,14 +15,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import CONST from '../const';
import getLevelFields from './getLevelFields';
import {LEVELS} from './index';
import CONST from "../const";
import getLevelFields from "./getLevelFields";
import { LEVELS } from "./index";
import createError from '../../../../../components/error';
import createError from "../../../../../components/error";
/** on-change event handler to update level */
const updateLevel = (e: Event) => {
const updateLevel = (e: Event): void => {
const target = <HTMLInputElement>e.target;
const id = target.id;
@ -36,7 +36,7 @@ const updateLevel = (e: Event) => {
}
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 {
@ -48,7 +48,7 @@ const updateLevel = (e: Event) => {
};
/** 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 difficultyID = CONST.DIFFICULTY_WITHOUT_LEVEL + id.toString();
@ -57,6 +57,6 @@ export const register = (id: number) => {
document.getElementById(difficultyID)
);
visitorElement.addEventListener('input', updateLevel, false);
difficultyElement.addEventListener('input', updateLevel, false);
visitorElement.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/>.
*/
import validateLevel from './validateLevel';
import {getAddForm, level1, fillAddLevel} from '../setupTests';
import setup from '../../../../../components/error/setUpTests';
import validateLevel from "./validateLevel";
import {getAddForm, level1, fillAddLevel} from "../setupTests";
import setup from "../../../../../components/error/setUpTests";
document.body.innerHTML = getAddForm();
document.body.appendChild(setup());
it('validate levels fields works', () => {
it("validate levels fields works", () => {
// null error
expect(validateLevel(1)).toEqual(false);

View File

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

View File

@ -14,13 +14,13 @@
* 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/>.
*/
import {LEVELS} from '../levels/index';
import updateLevelNumbersOnDOM from './updateDom';
import CONST from '../const';
import { LEVELS } from "../levels/index";
import updateLevelNumbersOnDOM from "./updateDom";
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
@ -31,12 +31,12 @@ const removeLevel = (e: Event) => {
const FIELDSET = <HTMLElement>PARENT.parentElement;
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)) {
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);
throw new Error(msg);
}
@ -47,19 +47,19 @@ const removeLevel = (e: Event) => {
};
/** adds onclick event listener */
export const addRemoveLevelButtonEventListener = (level: number) => {
export const addRemoveLevelButtonEventListener = (level: number): void => {
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 */
export const addRemoveLevelButtonEventListenerAll = () => {
export const addRemoveLevelButtonEventListenerAll = (): void => {
const removeButtons = document.querySelectorAll(`.${REMOVE_LEVEL_BUTTON}`);
removeButtons.forEach(button =>
button.addEventListener('click', removeLevel),
removeButtons.forEach((button) =>
button.addEventListener("click", removeLevel)
);
};
@ -67,20 +67,20 @@ export const addRemoveLevelButtonEventListenerAll = () => {
* Generate Remove button HTML. On-click handler should be added
* seprately
*/
export const getRemoveButtonHTML = (level: number) => {
export const getRemoveButtonHTML = (level: number): HTMLLabelElement => {
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.type = 'button';
btn.type = "button";
const id = `${CONST.REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL}${level}`;
btn.name = 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;
const removeLabelText = document.createTextNode('RemoveLevel');
const removeLabelText = document.createTextNode("RemoveLevel");
removeLabel.appendChild(removeLabelText);
removeLabel.appendChild(btn);
removeLabel.htmlFor = id;

View File

@ -15,17 +15,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import getNumLevels from '../levels/getNumLevels';
import {
getAddForm,
getRemoveButtonHTMLForm,
trim,
addLevel,
} from '../setupTests';
import CONST from '../const';
import getNumLevels from "../levels/getNumLevels";
import { getAddForm, addLevel } from "../setupTests";
import CONST from "../const";
import log from '../../../../../logger';
import {MODE} from '../../../../../logger';
import log from "../../../../../logger";
import { MODE } from "../../../../../logger";
document.body.innerHTML = getAddForm();
@ -46,13 +41,13 @@ const setUp = () => {
log.setMode(MODE.none);
it('removeLevelButton works', () => {
it("removeLevelButton works", () => {
setUp();
for (let i = 1; i < 4; i++) {
const l1 = <HTMLButtonElement>(
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
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import getNumLevels from '../../levels/getNumLevels';
import CONST from '../../const';
import log from '../../../../../../logger';
import getNumLevels from "../../levels/getNumLevels";
import CONST from "../../const";
import log from "../../../../../../logger";
import updateLabels from './updateLabel';
import updateInputs from './updateInputs';
import updateRemoveButton from './updateRemoveButton';
import updateLevelGroup from './updateLevelGroup';
import updateLabels from "./updateLabel";
import updateInputs from "./updateInputs";
import updateRemoveButton from "./updateRemoveButton";
import updateLevelGroup from "./updateLevelGroup";
/**
* update level number on fieldset legends and their ids too
* @param {number} id - level number that was ordered to remove.
* All updates are made relative to id
* */
const updateLevelNumbersOnDOM = (id: number) => {
const updateLevelNumbersOnDOM = (id: number): void => {
const numLevels = getNumLevels();
if (id == numLevels) {
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 levelGroup = document.querySelector(
`#${CONST.LEVEL_FIELDSET_ID_WITHOUT_LEVEL}${i}`,
`#${CONST.LEVEL_FIELDSET_ID_WITHOUT_LEVEL}${i}`
);
if (levelGroup === null) {
@ -53,9 +53,9 @@ const updateLevelNumbersOnDOM = (id: number) => {
}
// rename legend
const legend = levelGroup.getElementsByTagName('legend')[0];
const legend = levelGroup.getElementsByTagName("legend")[0];
const legendText = document.createTextNode(`Level ${newLevel}`);
const newLegend = document.createElement('legend');
const newLegend = document.createElement("legend");
newLegend.className = legend.className;
newLegend.appendChild(legendText);
legend.replaceWith(newLegend);

View File

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

View File

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

View File

@ -14,11 +14,11 @@
* 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/>.
*/
import CONST from '../../const';
import log from '../../../../../../logger';
import CONST from "../../const";
import log from "../../../../../../logger";
/** update input IDs with new level */
const updateInput = (levelGroup: Element, newLevel: number) => {
const updateInput = (levelGroup: Element, newLevel: number): void => {
const inputs = <NodeListOf<HTMLInputElement>>(
levelGroup.querySelectorAll(`.${CONST.LEVEL_INPUT_CLASS}`)
);
@ -26,17 +26,17 @@ const updateInput = (levelGroup: Element, newLevel: number) => {
inputs.forEach(input => {
if (input.id.includes(CONST.VISITOR_WITHOUT_LEVEL)) {
log.log(`${input.id}`);
log.log('changing visitor_threshold input');
log.log("changing visitor_threshold input");
const id = `${CONST.VISITOR_WITHOUT_LEVEL}${newLevel}`;
input.id = id;
input.name = id;
} 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}`;
input.id = id;
input.name = id;
} 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}`);
}
}

View File

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

View File

@ -14,10 +14,10 @@
* 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/>.
*/
import CONST from '../../const';
import CONST from "../../const";
/** 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}`);
export default updateLevelGroup;

View File

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

View File

@ -14,10 +14,10 @@
* 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/>.
*/
import CONST from '../../const';
import CONST from "../../const";
/** update remove level button's ID */
const updateRemoveButton = (levelGroup: Element, newLevel: number) => {
const updateRemoveButton = (levelGroup: Element, newLevel: number): void => {
// rename button
const button = <HTMLInputElement>(
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
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import getNumLevels from './levels/getNumLevels';
import {Level} from './levels/index';
import CONST from './const';
import addLevelButtonAddEventListener from './addLevelButton';
import getNumLevels from "./levels/getNumLevels";
import { Level } from "./levels/index";
import CONST from "./const";
import addLevelButtonAddEventListener from "./addLevelButton";
/** 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 = {
difficulty_factor: 200,
@ -43,7 +43,7 @@ export const level2: Level = {
};
/** 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);
const addLevelButton = <HTMLElement>(
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 */
export const fillAddLevel = (
visitor: number | string,
diff: number | string,
) => {
diff: number | string
): void => {
addLevelButtonAddEventListener();
const level = getNumLevels();
@ -71,7 +71,11 @@ export const fillAddLevel = (
};
/** 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) {
const visitorField = <HTMLInputElement>(
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 */
export const fillDescription = (description: string) => {
const inputElement = <HTMLInputElement>document.getElementById('description');
export const fillDescription = (description: string): void => {
const inputElement = <HTMLInputElement>document.getElementById("description");
inputElement.value = description;
};
/** Fill duration in add level form */
export const fillDuration = (duration: number | string) => {
const inputElement = <HTMLInputElement>document.getElementById('duration');
export const fillDuration = (duration: number | string): void => {
const inputElement = <HTMLInputElement>document.getElementById("duration");
inputElement.value = duration.toString();
};
export const getAddForm = () => `
export const getAddForm = (): string => `
<form class="sitekey-form" action="/api/v1/mcaptcha/levels/add" method="post">
<h1 class="form__title">
Add Sitekey
@ -171,7 +175,7 @@ export const getAddForm = () => `
`;
/** get initial form to test remove button functionality */
export const getRemoveButtonHTMLForm = () => {
export const getRemoveButtonHTMLForm = (): string => {
return `
<form class="sitekey-form" action="/api/v1/mcaptcha/levels/add" method="post">
<h1 class="form__title">

View File

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

View File

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

View File

@ -14,31 +14,31 @@
* 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/>.
*/
import * as Add from '../add/ts/form/';
import addLevelButtonAddEventListener from '../add/ts/addLevelButton';
import {addRemoveLevelButtonEventListenerAll} from '../add/ts/removeLevelButton';
import getNumLevels from '../add/ts/levels/getNumLevels';
import validateLevel from '../add/ts/levels/validateLevel';
import * as UpdateLevel from '../add/ts/levels/updateLevel';
import validateDescription from '../add/ts/form/validateDescription';
import validateDuration from '../add/ts/form/validateDuration';
import {LEVELS} from '../add/ts/levels';
import * as Add from "../add/ts/form/";
import addLevelButtonAddEventListener from "../add/ts/addLevelButton";
import { addRemoveLevelButtonEventListenerAll } from "../add/ts/removeLevelButton";
import getNumLevels from "../add/ts/levels/getNumLevels";
import validateLevel from "../add/ts/levels/validateLevel";
import * as UpdateLevel from "../add/ts/levels/updateLevel";
import validateDescription from "../add/ts/form/validateDescription";
import validateDuration from "../add/ts/form/validateDuration";
import { LEVELS } from "../add/ts/levels";
import getFormUrl from '../../../utils/getFormUrl';
import genJsonPayload from '../../../utils/genJsonPayload';
import createError from '../../../components/error';
import LazyElement from '../../../utils/lazyElement';
import getFormUrl from "../../../utils/getFormUrl";
import genJsonPayload from "../../../utils/genJsonPayload";
import createError from "../../../components/error";
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 submit = async (e: Event) => {
e.preventDefault();
const description = validateDescription(e);
const duration = validateDuration(e);
const duration = validateDuration();
const formUrl = getFormUrl(Add.FORM);
@ -66,7 +66,7 @@ const submit = async (e: Event) => {
};
const addSubmitEventListener = () => {
Add.FORM.addEventListener('submit', submit, true);
Add.FORM.addEventListener("submit", submit, true);
};
const bootstrapLevels = () => {
@ -78,7 +78,7 @@ const bootstrapLevels = () => {
}
};
export const index = () => {
export const index = (): void => {
addSubmitEventListener();
addLevelButtonAddEventListener();
bootstrapLevels();

View File

@ -14,17 +14,17 @@
* 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/>.
*/
import CopyIcon from '../../../../components/clipboard/';
import CopyIcon from "../../../../components/clipboard/";
const SITEKEY_COPY_ICON = `sitekey__copy-icon`;
const SITEKEY_COPY_DONE_ICON = `sitekey__copy-done-icon`;
const SITEKEY_COPY_ICON = "sitekey__copy-icon";
const SITEKEY_COPY_DONE_ICON = "sitekey__copy-done-icon";
export const index = () => {
export const index = (): void => {
const image = document.querySelectorAll(`.${SITEKEY_COPY_ICON}`);
image.forEach((img: HTMLElement) => {
if (!img.classList.contains(SITEKEY_COPY_ICON)) {
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;

View File

@ -14,4 +14,4 @@
* 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/>.
*/
export const index = () => {};
//export const index = () => {};

View File

@ -15,8 +15,6 @@
* 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 = () => {
listSitekeys.index();
};
export const index = (): void => listSitekeys.index();

View File

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

View File

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

View File

@ -16,7 +16,7 @@
*/
/** get login form HTML */
export const getLoginFormHtml = () =>
export const getLoginFormHtml = (): string =>
`
<form method="POST" action="/something" id="form">
<label class="form__in-group" for="username"
@ -51,7 +51,7 @@ export const getLoginFormHtml = () =>
`;
/** get registration form HTML */
export const getRegistrationFormHtml = () => `
export const getRegistrationFormHtml = (): string => `
<form method="POST" action="/api/v1/signup" class="form__box" id="form">
<label class="form__in-group" for="username"
>Username
@ -104,7 +104,7 @@ export const getRegistrationFormHtml = () => `
</form>
`;
export const mockAlert = () => {
export const mockAlert = (): void => {
delete window.alert;
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/>.
*/
import genJsonPayload from './genJsonPayload';
import genJsonPayload from "./genJsonPayload";
'use strict';
"use strict";
const payload = {
username: 'Jhon',
username: "Jhon",
};
const value = {
method: 'POST',
method: "POST",
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
};
it('getFromUrl workds', () => {
it("getFromUrl workds", () => {
expect(genJsonPayload(payload)).toEqual(value);
});

View File

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

View File

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

View File

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

View File

@ -15,22 +15,22 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import isBlankString from './isBlankString';
import {mockAlert} from '../setUpTests';
import isBlankString from "./isBlankString";
import {mockAlert} from "../setUpTests";
import setup from '../components/error/setUpTests';
import setup from "../components/error/setUpTests";
'use strict';
"use strict";
mockAlert();
it('getFromUrl workds', () => {
document.querySelector('body').appendChild(setup());
expect(isBlankString('test', 'username')).toBe(false);
it("getFromUrl workds", () => {
document.querySelector("body").appendChild(setup());
expect(isBlankString("test", "username")).toBe(false);
try {
isBlankString(' ', 'username');
isBlankString(" ", "username");
} 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
* 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();
if (!value.replace(/\s/g, '').length) {
if (!value.replace(/\s/g, "").length) {
if (event !== undefined) {
event.preventDefault();
}

View File

@ -15,14 +15,14 @@
* 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', () => {
expect(isNumber('test')).toBe(false);
expect(isNumber('1test213')).toBe(false);
it("getFromUrl workds", () => {
expect(isNumber("test")).toBe(false);
expect(isNumber("1test213")).toBe(false);
expect(isNumber('12')).toBe(true);
expect(isNumber("12")).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/>.
*/
const isNumber = (value: string|number) => {
const isNumber = (value: string|number): boolean => {
value = value.toString();
return /^\d+$/.test(value);
};

View File

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

View File

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

View File

@ -14,7 +14,7 @@
* 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/>.
*/
import './main.scss';
import "./main.scss";
//import prove from './runner/prove';
//import fetchPoWConfig from './runner/fetchPoWConfig';
//import sendWork from './runner/sendWork';

View File

@ -8,19 +8,19 @@
* 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 LazyElement from '../../utils/lazyElement';
import LazyElement from "../../utils/lazyElement";
/** mcaptcha checkbox ID **/
export const btnId = 'widget__verification-checkbox';
export const btnId = "widget__verification-checkbox";
/** get sitekey */
export const sitekey = () => {
export const sitekey = (): string => {
let sitekey;
return (() => {
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) {
throw new Error(`Define sitekey in query parameter`);
throw new Error("Define sitekey in query parameter");
}
}
return sitekey;
@ -29,8 +29,8 @@ export const sitekey = () => {
/** mCaptcha API routes */
export const ROUTES = (() => {
const getConfig = '/api/v1/pow/config';
const verififyPoW = '/api/v1/pow/verify';
const getConfig = "/api/v1/pow/config";
const verififyPoW = "/api/v1/pow/verify";
return {
/** get URL to fetch PoW configuration */
@ -41,24 +41,31 @@ export const ROUTES = (() => {
})();
/** get mCaptcha verifify checkbox button */
export const btn = () => {
export const btn = (): HTMLInputElement => {
let btn;
return (() => {
if (btn === null || btn === undefined) {
btn = <HTMLInputElement>document.getElementById(btnId);
if (btn === null || btn === undefined) {
throw new Error(`mCaptcha button not found)`);
throw new Error("mCaptcha button not found)");
}
}
return btn;
})();
};
export const messageText = () => {
const beforeID = 'widget__verification-text--before';
const duringID = 'widget__verification-text--during';
const errorID = 'widget__verification-text--error';
const afterID = 'widget__verification-text--after';
type messageTextReturn = {
before: () => void;
after: () => void;
during: () => void;
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 after = new LazyElement(afterID);
@ -70,9 +77,9 @@ export const messageText = () => {
// let error: 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 **/
const hideMsg = (e: HTMLElement) => (e.style.display = 'none');
const hideMsg = (e: HTMLElement) => (e.style.display = "none");
return {
/** 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.
*/
import genJsonPayload from './utils/genJsonPayload';
import * as CONST from './const';
import genJsonPayload from "../../utils/genJsonPayload";
import * as CONST from "./const";
type GetConfigPayload = {
key: string;
@ -26,22 +26,18 @@ export type PoWConfig = {
* fetch proof-of-work configuration
* @returns {PoWConfig} pow config
* */
export const fetchPoWConfig = async () => {
try {
const payload: GetConfigPayload = {
key: CONST.sitekey(),
};
export const fetchPoWConfig = async (): Promise<PoWConfig> => {
const payload: GetConfigPayload = {
key: CONST.sitekey(),
};
const res = await fetch(CONST.ROUTES.getConfig, genJsonPayload(payload));
if (res.ok) {
const config: PoWConfig = await res.json();
return config;
} else {
const err = await res.json();
throw new Error(err);
}
} catch (err) {
throw err;
const res = await fetch(CONST.ROUTES.getConfig, genJsonPayload(payload));
if (res.ok) {
const config: PoWConfig = await res.json();
return config;
} else {
const err = await res.json();
throw new Error(err);
}
};

View File

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

View File

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

View File

@ -8,15 +8,15 @@
* 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 {Token} from './sendWork';
import {Token} from "./sendWork";
/**
* send pow validation token as message to parant of the iframe
* @param {Token} token: token received from mCaptcha service
* upon successful PoW validation
* */
export const sendToParent = (token: Token) => {
window.parent.postMessage(token, '*');
export const sendToParent = (token: Token): void => {
window.parent.postMessage(token, "*");
// TODO set origin. Make parent send origin as query parameter
// or as a message to iframe
};

View File

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

View File

@ -8,13 +8,13 @@
* 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 * as CONST from '../const';
import * as CONST from "../const";
import {getBaseHtml, sitekey, checkbox} from './setupTests';
import * as TESTElements from './setupTests';
import {getBaseHtml, sitekey, checkbox} from "./setupTests";
import * as TESTElements from "./setupTests";
it('const works', () => {
const body = document.querySelector('body');
it("const works", () => {
const body = document.querySelector("body");
const container = getBaseHtml();
body.appendChild(container);
expect(CONST.sitekey()).toBe(sitekey);
@ -22,29 +22,29 @@ it('const works', () => {
// display after
CONST.messageText().after();
expect(TESTElements.afterMsg.style.display).toBe('block');
expect(TESTElements.beforeMsg.style.display).toBe('none');
expect(TESTElements.duringMsg.style.display).toBe('none');
expect(TESTElements.errorMsg.style.display).toBe('none');
expect(TESTElements.afterMsg.style.display).toBe("block");
expect(TESTElements.beforeMsg.style.display).toBe("none");
expect(TESTElements.duringMsg.style.display).toBe("none");
expect(TESTElements.errorMsg.style.display).toBe("none");
// display before
CONST.messageText().before();
expect(TESTElements.afterMsg.style.display).toBe('none');
expect(TESTElements.beforeMsg.style.display).toBe('block');
expect(TESTElements.duringMsg.style.display).toBe('none');
expect(TESTElements.errorMsg.style.display).toBe('none');
expect(TESTElements.afterMsg.style.display).toBe("none");
expect(TESTElements.beforeMsg.style.display).toBe("block");
expect(TESTElements.duringMsg.style.display).toBe("none");
expect(TESTElements.errorMsg.style.display).toBe("none");
// display during
CONST.messageText().during();
expect(TESTElements.afterMsg.style.display).toBe('none');
expect(TESTElements.beforeMsg.style.display).toBe('none');
expect(TESTElements.duringMsg.style.display).toBe('block');
expect(TESTElements.errorMsg.style.display).toBe('none');
expect(TESTElements.afterMsg.style.display).toBe("none");
expect(TESTElements.beforeMsg.style.display).toBe("none");
expect(TESTElements.duringMsg.style.display).toBe("block");
expect(TESTElements.errorMsg.style.display).toBe("none");
// display error
CONST.messageText().error();
expect(TESTElements.afterMsg.style.display).toBe('none');
expect(TESTElements.beforeMsg.style.display).toBe('none');
expect(TESTElements.duringMsg.style.display).toBe('none');
expect(TESTElements.errorMsg.style.display).toBe('block');
expect(TESTElements.afterMsg.style.display).toBe("none");
expect(TESTElements.beforeMsg.style.display).toBe("none");
expect(TESTElements.duringMsg.style.display).toBe("none");
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
* 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');
checkbox.type = 'checkbox';
export const checkbox = <HTMLInputElement>document.createElement("input");
checkbox.type = "checkbox";
checkbox.id = CONST.btnId;
const getMessages = (state: string) => {
const msg = <HTMLElement>document.createElement('span');
const msg = <HTMLElement>document.createElement("span");
msg.id = `widget__verification-text--${state}`;
return msg;
};
export const beforeMsg = getMessages('before');
export const afterMsg = getMessages('after');
export const duringMsg = getMessages('during');
export const errorMsg = getMessages('error');
export const beforeMsg = getMessages("before");
export const afterMsg = getMessages("after");
export const duringMsg = getMessages("during");
export const errorMsg = getMessages("error");
/** get base HTML with empty mCaptcha container */
export const getBaseHtml = () => {
const form = <HTMLFormElement>document.createElement('form');
export const getBaseHtml = (): HTMLFormElement => {
const form = <HTMLFormElement>document.createElement("form");
form.appendChild(checkbox);
form.appendChild(beforeMsg);
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