diff --git a/templates/panel/sitekey/add/ts/form.ts b/templates/panel/sitekey/add/ts/form.ts
deleted file mode 100644
index c5425d2d..00000000
--- a/templates/panel/sitekey/add/ts/form.ts
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2021  Aravinth Manivannan <realaravinth@batsense.net>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * 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';
-
-import isBlankString from '../../../../utils/isBlankString';
-import getFormUrl from '../../../../utils/getFormUrl';
-import genJsonPayload from '../../../../utils/genJsonPayload';
-import isNumber from '../../../../utils/isNumber';
-
-import VIEWS from '../../../../views/v1/routes';
-
-const SITE_KEY_FORM_CLASS = 'sitekey-form';
-const FORM = <HTMLFormElement>document.querySelector(`.${SITE_KEY_FORM_CLASS}`);
-//const FORM_SUBMIT_BUTTON_CLASS = "sitekey-form__submit";
-//const FORM_SUBMIT_BUTTON = <HTMLButtonElement>document.querySelector(`.${FORM_SUBMIT_BUTTON_CLASS}`);
-
-const addSubmitEventListener = () => {
-  FORM.addEventListener('submit', submit, true);
-};
-
-//const validateLevels = (e: Event) => {
-//  const numLevels = getNumLevels();
-//  // check if levels are unique and are in increasing order;
-//  // also if they are positive
-//  // also if level input field is accompanied by a "Add Level" button,
-//  // it shouldn't be used for validation
-//  for (let levelNum = 1; levelNum < numLevels; levelNum++) {
-//    const inputID = CONST.INPUT_ID_WITHOUT_LEVEL + levelNum;
-//    const inputElement = <HTMLInputElement>document.getElementById(inputID);
-//    const val = inputElement.value;
-//    const filed = CONST.LABEL_INNER_TEXT_WITHOUT_LEVEL + levelNum;
-//    isBlankString(val, filed, e);
-//  }
-//};
-
-const validateDescription = (e: Event) => {
-  const inputElement = <HTMLInputElement>document.getElementById('description');
-  const val = inputElement.value;
-  const filed = 'Description';
-  isBlankString(val, filed, e);
-  return val;
-};
-
-const validateDuration = (e: Event) => {
-  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');
-  }
-
-  if (duration <= 0) {
-    throw new Error('duration must be greater than zero');
-  }
-  return duration;
-};
-
-const submit = async (e: Event) => {
-  e.preventDefault();
-
-  const description = validateDescription(e);
-  const duration = validateDuration(e);
-
-  const formUrl = getFormUrl(FORM);
-
-  const levels = LEVELS.getLevels();
-  console.debug(`[form submition]: levels: ${levels}`);
-
-  const payload = {
-    levels: levels,
-    duration,
-    description,
-  };
-
-  console.debug(`[form submition] json payload: ${JSON.stringify(payload)}`);
-
-  const res = await fetch(formUrl, genJsonPayload(payload));
-  if (res.ok) {
-    alert('success');
-    window.location.assign(VIEWS.sitekey);
-  } else {
-    const err = await res.json();
-    alert(`error: ${err.error}`);
-  }
-};
-
-export default addSubmitEventListener;
diff --git a/templates/panel/sitekey/add/ts/form/index.ts b/templates/panel/sitekey/add/ts/form/index.ts
new file mode 100644
index 00000000..c91bea96
--- /dev/null
+++ b/templates/panel/sitekey/add/ts/form/index.ts
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021  Aravinth Manivannan <realaravinth@batsense.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * 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';
+
+import getFormUrl from '../../../../../utils/getFormUrl';
+import genJsonPayload from '../../../../../utils/genJsonPayload';
+
+import VIEWS from '../../../../../views/v1/routes';
+
+import validateDescription from './validateDescription';
+import validateDuration from './validateDuration';
+
+const SITE_KEY_FORM_CLASS = 'sitekey-form';
+const FORM = <HTMLFormElement>document.querySelector(`.${SITE_KEY_FORM_CLASS}`);
+
+const addSubmitEventListener = () => {
+  FORM.addEventListener('submit', submit, true);
+};
+
+const submit = async (e: Event) => {
+  e.preventDefault();
+
+  const description = validateDescription(e);
+  const duration = validateDuration(e);
+
+  const formUrl = getFormUrl(FORM);
+
+  const levels = LEVELS.getLevels();
+  console.debug(`[form submition]: levels: ${levels}`);
+
+  const payload = {
+    levels: levels,
+    duration,
+    description,
+  };
+
+  console.debug(`[form submition] json payload: ${JSON.stringify(payload)}`);
+
+  const res = await fetch(formUrl, genJsonPayload(payload));
+  if (res.ok) {
+    alert('success');
+    window.location.assign(VIEWS.sitekey);
+  } else {
+    const err = await res.json();
+    alert(`error: ${err.error}`);
+  }
+};
+
+export default addSubmitEventListener;
diff --git a/templates/panel/sitekey/add/ts/form/validateDescription.test.ts b/templates/panel/sitekey/add/ts/form/validateDescription.test.ts
new file mode 100644
index 00000000..67356016
--- /dev/null
+++ b/templates/panel/sitekey/add/ts/form/validateDescription.test.ts
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021  Aravinth Manivannan <realaravinth@batsense.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * 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 validateDescription from './validateDescription';
+import {getAddForm, fillDescription} from '../setupTests';
+
+document.body.innerHTML = getAddForm();
+
+const emptyErr = "can't be empty";
+
+it('validateDescription workds', () => {
+  try {
+    const event = new Event('submit');
+    validateDescription(event);
+  } catch (e) {
+    expect(e.message).toContain(emptyErr);
+  }
+
+  // fill and validate
+  fillDescription('testing');
+  const event = new Event('submit');
+  validateDescription(event);
+});
diff --git a/templates/panel/sitekey/add/ts/form/validateDescription.ts b/templates/panel/sitekey/add/ts/form/validateDescription.ts
new file mode 100644
index 00000000..3ceccf16
--- /dev/null
+++ b/templates/panel/sitekey/add/ts/form/validateDescription.ts
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021  Aravinth Manivannan <realaravinth@batsense.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * 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 isBlankString from '../../../../../utils/isBlankString';
+
+const validateDescription = (e: Event) => {
+  const inputElement = <HTMLInputElement>document.getElementById('description');
+  const val = inputElement.value;
+  const filed = 'Description';
+  isBlankString(val, filed, e);
+  return val;
+};
+
+export default validateDescription;
diff --git a/templates/panel/sitekey/add/ts/form/validateDuration.test.ts b/templates/panel/sitekey/add/ts/form/validateDuration.test.ts
new file mode 100644
index 00000000..057c56c1
--- /dev/null
+++ b/templates/panel/sitekey/add/ts/form/validateDuration.test.ts
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021  Aravinth Manivannan <realaravinth@batsense.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * 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);
+//  if (!isNumber(duration) || Number.isNaN(duration)) {
+//    throw new Error('duration can contain nubers only');
+//  }
+//
+//  if (duration <= 0) {
+//    throw new Error('duration must be greater than zero');
+//  }
+//  return duration;
+//};
+//
+//export default validateDuration;
+
+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 duration = 30;
+
+it('validateDuration workds', () => {
+  try {
+    const event = new Event('submit');
+    validateDuration(event);
+  } catch (e) {
+    expect(e.message).toContain(emptyErr);
+  }
+
+  // fill string error
+  try {
+    fillDuration('testing');
+    const event = new Event('submit');
+    validateDuration(event);
+  } catch (e) {
+    expect(e.message).toContain(NaNErr);
+  }
+
+  // zero err
+  try {
+    fillDuration(0);
+    const event = new Event('submit');
+    validateDuration(event);
+  } catch (e) {
+    expect(e.message).toContain(zeroErr);
+  }
+
+  fillDuration(duration);
+  const event = new Event('submit');
+  expect(validateDuration(event)).toBe(duration);
+});
diff --git a/templates/panel/sitekey/add/ts/form/validateDuration.ts b/templates/panel/sitekey/add/ts/form/validateDuration.ts
new file mode 100644
index 00000000..8de3e5c0
--- /dev/null
+++ b/templates/panel/sitekey/add/ts/form/validateDuration.ts
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021  Aravinth Manivannan <realaravinth@batsense.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * 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);
+  if (!isNumber(duration) || Number.isNaN(duration)) {
+    throw new Error('duration can contain nubers only');
+  }
+
+  if (duration <= 0) {
+    throw new Error('duration must be greater than zero');
+  }
+  return duration;
+};
+
+export default validateDuration;
diff --git a/templates/panel/sitekey/add/ts/levels/getLevelFields.test.ts b/templates/panel/sitekey/add/ts/levels/getLevelFields.test.ts
index 27258ff7..78cf7fbf 100644
--- a/templates/panel/sitekey/add/ts/levels/getLevelFields.test.ts
+++ b/templates/panel/sitekey/add/ts/levels/getLevelFields.test.ts
@@ -16,14 +16,39 @@
  */
 
 import getLevelFields from './getLevelFields';
-import {getAddForm, level1, level2, addLevel} from '../setupTests';
+import {
+  getAddForm,
+  level1,
+  level2,
+  fillAddLevel,
+  addLevel,
+} from '../setupTests';
 
 document.body.innerHTML = getAddForm();
 
+const visNumErr = 'visitor can contain nubers only';
+const diffNumErr = 'difficulty can contain nubers only';
+
 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);
+    getLevelFields(2);
+  } catch (e) {
+    expect(e.message).toBe(visNumErr);
+  }
+
+  // Nan difficulty_factor
+  try {
+    fillAddLevel(level2.visitor_threshold, 'fooasdads');
+    getLevelFields(2);
+  } catch (e) {
+    expect(e.message).toBe(diffNumErr);
+  }
+
   addLevel(level2.visitor_threshold, level2.difficulty_factor);
   expect(getLevelFields(2)).toEqual(level2);
 });
diff --git a/templates/panel/sitekey/add/ts/setupTests.ts b/templates/panel/sitekey/add/ts/setupTests.ts
index 43a3a9aa..cda0b547 100644
--- a/templates/panel/sitekey/add/ts/setupTests.ts
+++ b/templates/panel/sitekey/add/ts/setupTests.ts
@@ -19,6 +19,80 @@ import {Level} from './levels/index';
 import CONST from './const';
 import addLevelButtonAddEventListener from './addLevelButton';
 
+export const level1: Level = {
+  difficulty_factor: 200,
+  visitor_threshold: 500,
+};
+
+export const level1diffErr: Level = {
+  difficulty_factor: 100,
+  visitor_threshold: 600,
+};
+
+export const level1visErr: Level = {
+  difficulty_factor: 600,
+  visitor_threshold: 400,
+};
+
+export const level2: Level = {
+  difficulty_factor: 400,
+  visitor_threshold: 700,
+};
+
+/** add level to DOM by filling add level form and clicking "Add" button */
+export const addLevel = (visitor: number, diff: number) => {
+  fillAddLevel(visitor, diff);
+  const addLevelButton = <HTMLElement>(
+    document.querySelector(`.${CONST.ADD_LEVEL_BUTTON}`)
+  );
+  addLevelButton.click();
+};
+
+/** Fill add level form without clicking add button */
+export const fillAddLevel = (visitor: number|string, diff: number|string) => {
+  addLevelButtonAddEventListener();
+
+  const level = getNumLevels();
+  const visitorField = <HTMLInputElement>(
+    document.getElementById(`${CONST.VISITOR_WITHOUT_LEVEL}${level}`)
+  );
+  visitorField.value = visitor.toString();
+
+  const diffField = <HTMLInputElement>(
+    document.getElementById(`${CONST.DIFFICULTY_WITHOUT_LEVEL}${level}`)
+  );
+  diffField.value = diff.toString();
+};
+
+/** Fill add level form without clicking add button */
+export const editLevel = (level: number, visitor?: number, diff?: number) => {
+  if (visitor !== undefined) {
+    const visitorField = <HTMLInputElement>(
+      document.getElementById(`${CONST.VISITOR_WITHOUT_LEVEL}${level}`)
+    );
+    visitorField.value = visitor.toString();
+  }
+
+  if (diff !== undefined) {
+    const diffField = <HTMLInputElement>(
+      document.getElementById(`${CONST.DIFFICULTY_WITHOUT_LEVEL}${level}`)
+    );
+    diffField.value = diff.toString();
+  }
+};
+
+/** Fill description in add level form */
+export const fillDescription = (description: string) => {
+  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');
+  inputElement.value = duration.toString();
+};
+
 export const getAddForm = () => `
 <form class="sitekey-form" action="/api/v1/mcaptcha/levels/add" method="post">
   <h1 class="form__title">
@@ -89,65 +163,3 @@ export const getAddForm = () => `
   <button class="sitekey-form__submit" type="submit">Submit</button>
 </form>
 `;
-
-/** add level to DOM by filling add level form and clicking "Add" button */
-export const addLevel = (visitor: number, diff: number) => {
-  fillAddLevel(visitor, diff);
-  const addLevelButton = <HTMLElement>(
-    document.querySelector(`.${CONST.ADD_LEVEL_BUTTON}`)
-  );
-  addLevelButton.click();
-};
-
-/** Fill add level form without clicking add button */
-export const fillAddLevel = (visitor: number, diff: number) => {
-  addLevelButtonAddEventListener();
-
-  const level = getNumLevels();
-  const visitorField = <HTMLInputElement>(
-    document.getElementById(`${CONST.VISITOR_WITHOUT_LEVEL}${level}`)
-  );
-  visitorField.value = visitor.toString();
-
-  const diffField = <HTMLInputElement>(
-    document.getElementById(`${CONST.DIFFICULTY_WITHOUT_LEVEL}${level}`)
-  );
-  diffField.value = diff.toString();
-};
-
-/** Fill add level form without clicking add button */
-export const editLevel = (level: number, visitor?: number, diff?: number) => {
-  if (visitor !== undefined) {
-    const visitorField = <HTMLInputElement>(
-      document.getElementById(`${CONST.VISITOR_WITHOUT_LEVEL}${level}`)
-    );
-    visitorField.value = visitor.toString();
-  }
-
-  if (diff !== undefined) {
-    const diffField = <HTMLInputElement>(
-      document.getElementById(`${CONST.DIFFICULTY_WITHOUT_LEVEL}${level}`)
-    );
-    diffField.value = diff.toString();
-  }
-};
-
-export const level1: Level = {
-  difficulty_factor: 200,
-  visitor_threshold: 500,
-};
-
-export const level1diffErr: Level = {
-  difficulty_factor: 100,
-  visitor_threshold: 600,
-};
-
-export const level1visErr: Level = {
-  difficulty_factor: 600,
-  visitor_threshold: 400,
-};
-
-export const level2: Level = {
-  difficulty_factor: 400,
-  visitor_threshold: 700,
-};
diff --git a/templates/router.test.ts b/templates/router.test.ts
index ba9f3ce3..0e03facd 100644
--- a/templates/router.test.ts
+++ b/templates/router.test.ts
@@ -31,6 +31,10 @@ const settingsRoute = '/settings/';
 const settingsResult = 'hello from settings';
 const settings = () => (result.result = settingsResult);
 
+const UriExistsErr = 'URI exists';
+const emptyUriErr = 'uri is empty';
+const unregisteredRouteErr = "Route isn't registered";
+
 const router = new Router();
 router.register(panelRoute, panel);
 router.register(settingsRoute, settings);
@@ -43,9 +47,25 @@ it('checks if Router works', () => {
   router.route();
   expect(result.result).toBe(panelResult);
 
+  // duplicate URI registration
   try {
     router.register(settingsRoute, settings);
   } catch (e) {
-    expect(e.message).toBe('URI exists');
+    expect(e.message).toBe(UriExistsErr);
+  }
+
+  // empty URI registration
+  try {
+    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`);
+    router.route();
+  } catch (e) {
+    expect(e.message).toBe(unregisteredRouteErr);
   }
 });
diff --git a/templates/router.ts b/templates/router.ts
index e803fdd6..e1d1602c 100644
--- a/templates/router.ts
+++ b/templates/router.ts
@@ -17,19 +17,16 @@
 
 /** Removes trailing slashed from URI */
 const normalizeUri = (uri: string) => {
-  if (typeof uri == 'string') {
-    if (uri.trim().length == 0) {
-      throw new Error('uri is empty');
-    }
-
-    let uriLength = uri.length;
-    if (uri[uriLength - 1] == '/') {
-      uri = uri.slice(0, uriLength - 1);
-    }
-    return uri;
-  } else {
-    throw new TypeError(`Only strings are permitted in URI`);
+  uri = uri.trim();
+  if (uri.length == 0) {
+    throw new Error('uri is empty');
   }
+
+  let uriLength = uri.length;
+  if (uri[uriLength - 1] == '/') {
+    uri = uri.slice(0, uriLength - 1);
+  }
+  return uri;
 };
 
 /** URI<-> Fn mapping type */
@@ -55,28 +52,17 @@ export class Router {
    * matches uri
    * */
   register(uri: string, fn: () => void) {
-    // typechecks
-    if (uri.trim().length == 0) {
-      throw new Error('uri is empty');
-    }
-
-    if (typeof uri !== 'string') {
-      throw new TypeError('URI must be a string');
-    }
-
-    if (typeof fn !== 'function') {
-      throw new TypeError('a callback fn must be provided');
-    }
-
     uri = normalizeUri(uri);
 
-    if (this.routes.find(route => {
-      if (route.uri == uri) {
-        return true;
-      }
-    })) {
-        throw new Error('URI exists');
-    };
+    if (
+      this.routes.find(route => {
+        if (route.uri == uri) {
+          return true;
+        }
+      })
+    ) {
+      throw new Error('URI exists');
+    }
 
     const route: routeTuple = {
       uri,
@@ -92,11 +78,19 @@ export class Router {
   route() {
     const path = normalizeUri(window.location.pathname);
 
+    let fn: () => void | undefined;
+
     this.routes.forEach(route => {
       const pattern = new RegExp(`^${route.uri}$`);
       if (path.match(pattern)) {
-        return route.fn();
+       fn =  route.fn;
       }
     });
+
+    if (fn === undefined) {
+    throw new Error("Route isn't registered");
+    }
+
+    return fn();
   }
 }
diff --git a/templates/utils/getFromUrl.test.ts b/templates/utils/getFormUrl.test.ts
similarity index 88%
rename from templates/utils/getFromUrl.test.ts
rename to templates/utils/getFormUrl.test.ts
index 81101ca4..f92f597d 100644
--- a/templates/utils/getFromUrl.test.ts
+++ b/templates/utils/getFormUrl.test.ts
@@ -23,6 +23,8 @@ import {getLoginFormHtml} from '../setUpTests';
 const formClassName = 'form__box';
 const formURL = '/api/v1/signin';
 
+const noFormErr = "Can't find form";
+
 document.body.innerHTML = getLoginFormHtml();
 
 const form = document.querySelector('form');
@@ -37,4 +39,11 @@ it('getFromUrl workds', () => {
   expect(getFormUrl(form)).toContain(formURL);
 
   expect(getFormUrl()).toContain(formURL);
+
+  try {
+    document.body.innerHTML = formURL;
+    getFormUrl();
+  } catch (e) {
+    expect(e.message).toContain(noFormErr);
+  }
 });
diff --git a/templates/utils/getFormUrl.ts b/templates/utils/getFormUrl.ts
index 679680bd..e1221154 100644
--- a/templates/utils/getFormUrl.ts
+++ b/templates/utils/getFormUrl.ts
@@ -33,7 +33,7 @@ const getFormUrl = (querySelector?: string | HTMLFormElement) => {
     form = querySelector;
   }
 
-  if (form !== undefined) {
+  if (form !== undefined && form !== null) {
     return form.action;
   } else {
     throw new Error("Can't find form");