diff --git a/.travis.yml b/.travis.yml
index 71799dd..e027586 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,5 +4,6 @@ node_js:
 script:
   - npm run format
   - npm run lint
+  - npm run test
 notifications:
   email: false
diff --git a/jest.config.js b/jest.config.js
new file mode 100644
index 0000000..ba36ec0
--- /dev/null
+++ b/jest.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+  ...require('@snowpack/app-scripts-preact/jest.config.js')(),
+};
diff --git a/package-lock.json b/package-lock.json
index f4340f8..88a9c01 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,9 +13,9 @@
       }
     },
     "@babel/compat-data": {
-      "version": "7.12.1",
-      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.1.tgz",
-      "integrity": "sha512-725AQupWJZ8ba0jbKceeFblZTY90McUBWMwHhkFQ9q1zKPJ95GUktljFcgcsIVwRnTnRKlcYzfiNImg5G9m6ZQ==",
+      "version": "7.12.5",
+      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.5.tgz",
+      "integrity": "sha512-DTsS7cxrsH3by8nqQSpFSyjSfSYl57D6Cf4q8dW3LK83tBKBDCkfcay1nYkXq1nIHXnpX8WMMb/O25HOy3h1zg==",
       "dev": true
     },
     "@babel/core": {
@@ -94,14 +94,14 @@
       }
     },
     "@babel/helper-compilation-targets": {
-      "version": "7.12.1",
-      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.1.tgz",
-      "integrity": "sha512-jtBEif7jsPwP27GPHs06v4WBV0KrE8a/P7n0N0sSvHn2hwUCYnolP/CLmz51IzAW4NlN+HuoBtb9QcwnRo9F/g==",
+      "version": "7.12.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.5.tgz",
+      "integrity": "sha512-+qH6NrscMolUlzOYngSBMIOQpKUGPPsc61Bu5W10mg84LxZ7cmvnBHzARKbDoFxVvqqAbj6Tg6N7bSrWSPXMyw==",
       "dev": true,
       "requires": {
-        "@babel/compat-data": "^7.12.1",
+        "@babel/compat-data": "^7.12.5",
         "@babel/helper-validator-option": "^7.12.1",
-        "browserslist": "^4.12.0",
+        "browserslist": "^4.14.5",
         "semver": "^5.5.0"
       }
     },
@@ -1414,21 +1414,21 @@
       "dev": true
     },
     "@jest/transform": {
-      "version": "26.6.0",
-      "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.0.tgz",
-      "integrity": "sha512-NUNA1NMCyVV9g5NIQF1jzW7QutQhB/HAocteCiUyH0VhmLXnGMTfPYQu1G6IjPk+k1SWdh2PD+Zs1vMqbavWzg==",
+      "version": "26.6.2",
+      "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz",
+      "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==",
       "dev": true,
       "requires": {
         "@babel/core": "^7.1.0",
-        "@jest/types": "^26.6.0",
+        "@jest/types": "^26.6.2",
         "babel-plugin-istanbul": "^6.0.0",
         "chalk": "^4.0.0",
         "convert-source-map": "^1.4.0",
         "fast-json-stable-stringify": "^2.0.0",
         "graceful-fs": "^4.2.4",
-        "jest-haste-map": "^26.6.0",
+        "jest-haste-map": "^26.6.2",
         "jest-regex-util": "^26.0.0",
-        "jest-util": "^26.6.0",
+        "jest-util": "^26.6.2",
         "micromatch": "^4.0.2",
         "pirates": "^4.0.1",
         "slash": "^3.0.0",
@@ -1436,6 +1436,19 @@
         "write-file-atomic": "^3.0.0"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "26.6.2",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
+          "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^3.0.0",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
         "ansi-styles": {
           "version": "4.3.0",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
@@ -1704,9 +1717,9 @@
       "dev": true
     },
     "@snowpack/app-scripts-react": {
-      "version": "1.12.4",
-      "resolved": "https://registry.npmjs.org/@snowpack/app-scripts-react/-/app-scripts-react-1.12.4.tgz",
-      "integrity": "sha512-zRUIKO1cWQoqTn5cRuZXzolXFjOvdtS7DgwBlNGzsjn8W8Ek2oGmIIL6BDxWrdy+WOiNGcfVF899w+hN4BP2yg==",
+      "version": "1.12.6",
+      "resolved": "https://registry.npmjs.org/@snowpack/app-scripts-react/-/app-scripts-react-1.12.6.tgz",
+      "integrity": "sha512-/Ew3fEYp1lCrRWg+e1ZBTLSi573oOPAG6bHC0Zw7nLFmVeHn+OVrAiyhl5jpboAeJlgE2g1rIAmse+CNlvBPSA==",
       "dev": true,
       "requires": {
         "@babel/core": "^7.10.5",
@@ -1714,11 +1727,30 @@
         "@babel/preset-typescript": "^7.10.4",
         "@snowpack/plugin-babel": "^2.1.4",
         "@snowpack/plugin-dotenv": "^2.0.4",
-        "@snowpack/plugin-react-refresh": "^2.3.3",
-        "@snowpack/plugin-typescript": "^1.0.2",
+        "@snowpack/plugin-react-refresh": "^2.3.5",
+        "@snowpack/plugin-typescript": "^1.1.0",
         "babel-jest": "^26.2.2",
         "babel-preset-react-app": "^9.1.2",
         "react-app-polyfill": "^1.0.6"
+      },
+      "dependencies": {
+        "@snowpack/plugin-react-refresh": {
+          "version": "2.3.6",
+          "resolved": "https://registry.npmjs.org/@snowpack/plugin-react-refresh/-/plugin-react-refresh-2.3.6.tgz",
+          "integrity": "sha512-7gxFXZljmhpzDtI9p8KM5AAqMJtjUlfnYQHcMhkHJbGtfbgbBVmrBqyR8LWrAvywovX7jaFm6bk79Uuq/E5v0Q==",
+          "dev": true,
+          "requires": {
+            "@babel/core": "^7.0.0",
+            "@babel/plugin-syntax-class-properties": "^7.10.0",
+            "react-refresh": "^0.9.0"
+          }
+        },
+        "react-refresh": {
+          "version": "0.9.0",
+          "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.9.0.tgz",
+          "integrity": "sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==",
+          "dev": true
+        }
       }
     },
     "@snowpack/plugin-babel": {
@@ -1773,9 +1805,9 @@
       }
     },
     "@snowpack/plugin-typescript": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/@snowpack/plugin-typescript/-/plugin-typescript-1.0.2.tgz",
-      "integrity": "sha512-qVmsecGwzx5bCI0RDDIri0jtxtTwCY3eA0Q7sQBJKpVhaZ75FPIR0JTRLcDkoQc2V6OO7qTbDoNdyGFQvv0AEg==",
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@snowpack/plugin-typescript/-/plugin-typescript-1.1.0.tgz",
+      "integrity": "sha512-6jAFFks42lW1Gy07qVF6A0RKOCQc+ICa029GUrbgLmXhs0UzDmTG+jJ0qyC41J9MnRR/utLrXvgWWVrmIrPifw==",
       "dev": true,
       "requires": {
         "execa": "^4.0.3",
@@ -1933,9 +1965,9 @@
       "dev": true
     },
     "@types/babel__core": {
-      "version": "7.1.10",
-      "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.10.tgz",
-      "integrity": "sha512-x8OM8XzITIMyiwl5Vmo2B1cR1S1Ipkyv4mdlbJjMa1lmuKvKY9FrBbEANIaMlnWn5Rf7uO+rC/VgYabNkE17Hw==",
+      "version": "7.1.12",
+      "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.12.tgz",
+      "integrity": "sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ==",
       "dev": true,
       "requires": {
         "@babel/parser": "^7.1.0",
@@ -1992,9 +2024,9 @@
       "dev": true
     },
     "@types/graceful-fs": {
-      "version": "4.1.3",
-      "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.3.tgz",
-      "integrity": "sha512-AiHRaEB50LQg0pZmm659vNBb9f4SJ0qrAnteuzhSeAUcJKxoYgEnprg/83kppCnc2zvtCKbdZry1a5pVY3lOTQ==",
+      "version": "4.1.4",
+      "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.4.tgz",
+      "integrity": "sha512-mWA/4zFQhfvOA8zWkXobwJvBD7vzcxgrOQ0J5CH1votGqdq9m7+FwtGaqyCZqC3NyyBkc9z4m+iry4LlqcMWJg==",
       "dev": true,
       "requires": {
         "@types/node": "*"
@@ -2901,21 +2933,34 @@
       }
     },
     "babel-jest": {
-      "version": "26.6.0",
-      "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.0.tgz",
-      "integrity": "sha512-JI66yILI7stzjHccAoQtRKcUwJrJb4oMIxLTirL3GdAjGpaUBQSjZDFi9LsPkN4gftsS4R2AThAJwOjJxadwbg==",
+      "version": "26.6.3",
+      "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz",
+      "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==",
       "dev": true,
       "requires": {
-        "@jest/transform": "^26.6.0",
-        "@jest/types": "^26.6.0",
+        "@jest/transform": "^26.6.2",
+        "@jest/types": "^26.6.2",
         "@types/babel__core": "^7.1.7",
         "babel-plugin-istanbul": "^6.0.0",
-        "babel-preset-jest": "^26.5.0",
+        "babel-preset-jest": "^26.6.2",
         "chalk": "^4.0.0",
         "graceful-fs": "^4.2.4",
         "slash": "^3.0.0"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "26.6.2",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
+          "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^3.0.0",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
         "ansi-styles": {
           "version": "4.3.0",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
@@ -2990,9 +3035,9 @@
       }
     },
     "babel-plugin-jest-hoist": {
-      "version": "26.5.0",
-      "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.5.0.tgz",
-      "integrity": "sha512-ck17uZFD3CDfuwCLATWZxkkuGGFhMij8quP8CNhwj8ek1mqFgbFzRJ30xwC04LLscj/aKsVFfRST+b5PT7rSuw==",
+      "version": "26.6.2",
+      "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz",
+      "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==",
       "dev": true,
       "requires": {
         "@babel/template": "^7.3.3",
@@ -3019,9 +3064,9 @@
       "dev": true
     },
     "babel-preset-current-node-syntax": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.4.tgz",
-      "integrity": "sha512-5/INNCYhUGqw7VbVjT/hb3ucjgkVHKXY7lX3ZjlN4gm565VyFmJUrJ/h+h16ECVB38R/9SF6aACydpKMLZ/c9w==",
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.0.tgz",
+      "integrity": "sha512-mGkvkpocWJes1CmMKtgGUwCeeq0pOhALyymozzDWYomHTbDLwueDYG6p4TK1YOeYHCzBzYPsWkgTto10JubI1Q==",
       "dev": true,
       "requires": {
         "@babel/plugin-syntax-async-generators": "^7.8.4",
@@ -3034,17 +3079,18 @@
         "@babel/plugin-syntax-numeric-separator": "^7.8.3",
         "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
         "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
-        "@babel/plugin-syntax-optional-chaining": "^7.8.3"
+        "@babel/plugin-syntax-optional-chaining": "^7.8.3",
+        "@babel/plugin-syntax-top-level-await": "^7.8.3"
       }
     },
     "babel-preset-jest": {
-      "version": "26.5.0",
-      "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.5.0.tgz",
-      "integrity": "sha512-F2vTluljhqkiGSJGBg/jOruA8vIIIL11YrxRcO7nviNTMbbofPSHwnm8mgP7d/wS7wRSexRoI6X1A6T74d4LQA==",
+      "version": "26.6.2",
+      "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz",
+      "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==",
       "dev": true,
       "requires": {
-        "babel-plugin-jest-hoist": "^26.5.0",
-        "babel-preset-current-node-syntax": "^0.1.3"
+        "babel-plugin-jest-hoist": "^26.6.2",
+        "babel-preset-current-node-syntax": "^1.0.0"
       }
     },
     "babel-preset-react-app": {
@@ -4401,15 +4447,46 @@
       "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA=="
     },
     "core-js-compat": {
-      "version": "3.6.5",
-      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz",
-      "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==",
+      "version": "3.7.0",
+      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.7.0.tgz",
+      "integrity": "sha512-V8yBI3+ZLDVomoWICO6kq/CD28Y4r1M7CWeO4AGpMdMfseu8bkSubBmUPySMGKRTS+su4XQ07zUkAsiu9FCWTg==",
       "dev": true,
       "requires": {
-        "browserslist": "^4.8.5",
+        "browserslist": "^4.14.6",
         "semver": "7.0.0"
       },
       "dependencies": {
+        "browserslist": {
+          "version": "4.14.7",
+          "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.7.tgz",
+          "integrity": "sha512-BSVRLCeG3Xt/j/1cCGj1019Wbty0H+Yvu2AOuZSuoaUWn3RatbL33Cxk+Q4jRMRAbOm0p7SLravLjpnT6s0vzQ==",
+          "dev": true,
+          "requires": {
+            "caniuse-lite": "^1.0.30001157",
+            "colorette": "^1.2.1",
+            "electron-to-chromium": "^1.3.591",
+            "escalade": "^3.1.1",
+            "node-releases": "^1.1.66"
+          }
+        },
+        "caniuse-lite": {
+          "version": "1.0.30001157",
+          "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001157.tgz",
+          "integrity": "sha512-gOerH9Wz2IRZ2ZPdMfBvyOi3cjaz4O4dgNwPGzx8EhqAs4+2IL/O+fJsbt+znSigujoZG8bVcIAUM/I/E5K3MA==",
+          "dev": true
+        },
+        "electron-to-chromium": {
+          "version": "1.3.591",
+          "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.591.tgz",
+          "integrity": "sha512-ol/0WzjL4NS4Kqy9VD6xXQON91xIihDT36sYCew/G/bnd1v0/4D+kahp26JauQhgFUjrdva3kRSo7URcUmQ+qw==",
+          "dev": true
+        },
+        "node-releases": {
+          "version": "1.1.66",
+          "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.66.tgz",
+          "integrity": "sha512-JHEQ1iWPGK+38VLB2H9ef2otU4l8s3yAMt9Xf934r6+ojCYDMHPMqvCc9TnzfeFSP1QEOeU6YZEd3+De0LTCgg==",
+          "dev": true
+        },
         "semver": {
           "version": "7.0.0",
           "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
@@ -7227,12 +7304,12 @@
       }
     },
     "jest-haste-map": {
-      "version": "26.6.0",
-      "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.0.tgz",
-      "integrity": "sha512-RpNqAGMR58uG9E9vWITorX2/R7he/tSbHWldX5upt1ymEcmCaXczqXxjqI6xOtRR8Ev6ZEYDfgSA5Fy7WHUL5w==",
+      "version": "26.6.2",
+      "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz",
+      "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==",
       "dev": true,
       "requires": {
-        "@jest/types": "^26.6.0",
+        "@jest/types": "^26.6.2",
         "@types/graceful-fs": "^4.1.2",
         "@types/node": "*",
         "anymatch": "^3.0.3",
@@ -7240,12 +7317,76 @@
         "fsevents": "^2.1.2",
         "graceful-fs": "^4.2.4",
         "jest-regex-util": "^26.0.0",
-        "jest-serializer": "^26.5.0",
-        "jest-util": "^26.6.0",
-        "jest-worker": "^26.5.0",
+        "jest-serializer": "^26.6.2",
+        "jest-util": "^26.6.2",
+        "jest-worker": "^26.6.2",
         "micromatch": "^4.0.2",
         "sane": "^4.0.3",
         "walker": "^1.0.7"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.6.2",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
+          "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^3.0.0",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
       }
     },
     "jest-regex-util": {
@@ -7255,9 +7396,9 @@
       "dev": true
     },
     "jest-serializer": {
-      "version": "26.5.0",
-      "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.5.0.tgz",
-      "integrity": "sha512-+h3Gf5CDRlSLdgTv7y0vPIAoLgX/SI7T4v6hy+TEXMgYbv+ztzbg5PSN6mUXAT/hXYHvZRWm+MaObVfqkhCGxA==",
+      "version": "26.6.2",
+      "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz",
+      "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==",
       "dev": true,
       "requires": {
         "@types/node": "*",
@@ -7265,12 +7406,12 @@
       }
     },
     "jest-util": {
-      "version": "26.6.0",
-      "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.0.tgz",
-      "integrity": "sha512-/cUGqcnKeZMjvTQLfJo65nBOEZ/k0RB/8usv2JpfYya05u0XvBmKkIH5o5c4nCh9DD61B1YQjMGGqh1Ha0aXdg==",
+      "version": "26.6.2",
+      "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz",
+      "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==",
       "dev": true,
       "requires": {
-        "@jest/types": "^26.6.0",
+        "@jest/types": "^26.6.2",
         "@types/node": "*",
         "chalk": "^4.0.0",
         "graceful-fs": "^4.2.4",
@@ -7278,6 +7419,19 @@
         "micromatch": "^4.0.2"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "26.6.2",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
+          "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^3.0.0",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
         "ansi-styles": {
           "version": "4.3.0",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
@@ -7330,9 +7484,9 @@
       }
     },
     "jest-worker": {
-      "version": "26.5.0",
-      "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.5.0.tgz",
-      "integrity": "sha512-kTw66Dn4ZX7WpjZ7T/SUDgRhapFRKWmisVAF0Rv4Fu8SLFD7eLbqpLvbxVqYhSgaWa7I+bW7pHnbyfNsH6stug==",
+      "version": "26.6.2",
+      "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz",
+      "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==",
       "dev": true,
       "requires": {
         "@types/node": "*",
@@ -9679,9 +9833,9 @@
       "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw=="
     },
     "regenerate": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz",
-      "integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==",
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
+      "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
       "dev": true
     },
     "regenerate-unicode-properties": {
@@ -12126,9 +12280,9 @@
       "dev": true
     },
     "whatwg-fetch": {
-      "version": "3.4.1",
-      "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.4.1.tgz",
-      "integrity": "sha512-sofZVzE1wKwO+EYPbWfiwzaKovWiZXf4coEzjGP9b2GBVgQRLQUZ2QcuPpQExGDAW5GItpEm6Tl4OU5mywnAoQ==",
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.5.0.tgz",
+      "integrity": "sha512-jXkLtsR42xhXg7akoDKvKWE40eJeI+2KZqcp2h3NsOrRnDvtWX36KcKl30dy+hxECivdk2BVUHVNrPtoMBUx6A==",
       "dev": true
     },
     "whatwg-url": {
@@ -12190,9 +12344,9 @@
       }
     },
     "workerpool": {
-      "version": "6.0.2",
-      "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz",
-      "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==",
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.3.tgz",
+      "integrity": "sha512-meU8ZzO+ipcx/njxtKUcbu2K95085q5WYDo8fR6PMW3hCY4driteIsNsEowYV7dzOtvq0HotUKsReJkK8gKXgg==",
       "dev": true
     },
     "wrap-ansi": {
diff --git a/package.json b/package.json
index 231de2c..694a36a 100644
--- a/package.json
+++ b/package.json
@@ -36,7 +36,7 @@
   },
   "devDependencies": {
     "@babel/preset-react": "^7.12.1",
-    "@snowpack/app-scripts-react": "^1.12.4",
+    "@snowpack/app-scripts-react": "^1.12.6",
     "@snowpack/plugin-dotenv": "^2.0.4",
     "@snowpack/plugin-react-refresh": "^2.3.4",
     "@snowpack/web-test-runner-plugin": "^0.1.3",
diff --git a/src/components/Canvas.jsx b/src/components/Canvas.jsx
index 657d831..7ca2881 100644
--- a/src/components/Canvas.jsx
+++ b/src/components/Canvas.jsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect } from 'react';
+import React, { useEffect } from 'react';
 import ReactFlow, {
   removeElements,
   addEdge,
diff --git a/src/test/AddPathBtn.test.tsx b/src/test/AddPathBtn.test.tsx
new file mode 100644
index 0000000..b54ba37
--- /dev/null
+++ b/src/test/AddPathBtn.test.tsx
@@ -0,0 +1,11 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import AddPathBtn from '../components/editor/AddPathBtn';
+
+describe('', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(, div);
+    ReactDOM.unmountComponentAtNode(div);
+  });
+});
\ No newline at end of file
diff --git a/src/test/App.test.tsx b/src/test/App.test.tsx
new file mode 100644
index 0000000..3a9c80a
--- /dev/null
+++ b/src/test/App.test.tsx
@@ -0,0 +1,20 @@
+// make React available
+import React from 'react';
+
+// make the ReactDOM available, necessary for rendering the component
+import ReactDOM from 'react-dom';
+
+// make the App component available
+import App from '../App';
+
+// this is the test case
+it('renders without crashing', () => {
+  // first create a DOM element to render the component into
+  const div = document.createElement('div');
+
+  // render the component, this is the actual test, if something is wrong it will fail here
+  ReactDOM.render(, div);
+
+  // clean up code
+  ReactDOM.unmountComponentAtNode(div);
+});
\ No newline at end of file
diff --git a/src/test/Description.test.tsx b/src/test/Description.test.tsx
new file mode 100644
index 0000000..8cc2418
--- /dev/null
+++ b/src/test/Description.test.tsx
@@ -0,0 +1,12 @@
+import { expect } from 'chai';
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Description from '../components/editor/Description';
+
+describe('', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(, div);
+    ReactDOM.unmountComponentAtNode(div);
+  });
+});
\ No newline at end of file
diff --git a/src/test/Layout.test.tsx b/src/test/Layout.test.tsx
new file mode 100644
index 0000000..b5d4705
--- /dev/null
+++ b/src/test/Layout.test.tsx
@@ -0,0 +1,12 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { ElementsProvider } from '../hooks/useElements';
+import Layout from '../Layout';
+
+describe('', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(, div);
+    ReactDOM.unmountComponentAtNode(div);
+  });
+});
\ No newline at end of file
diff --git a/src/test/MethodSelect.test.tsx b/src/test/MethodSelect.test.tsx
new file mode 100644
index 0000000..a075243
--- /dev/null
+++ b/src/test/MethodSelect.test.tsx
@@ -0,0 +1,11 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import MethodSelect from '../components/editor/MethodSelect';
+
+describe('', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(, div);
+    ReactDOM.unmountComponentAtNode(div);
+  });
+});
\ No newline at end of file
diff --git a/src/test/Path.test.tsx b/src/test/Path.test.tsx
new file mode 100644
index 0000000..5e897e5
--- /dev/null
+++ b/src/test/Path.test.tsx
@@ -0,0 +1,11 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Path from '../components/editor/Path';
+
+describe('', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(, div);
+    ReactDOM.unmountComponentAtNode(div);
+  });
+});
\ No newline at end of file