From e302e731e11bc00b3f268ed38c3dce5590d53299 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakob=20Karevold=20Gr=C3=B8nhaug?= <jakobkg@stud.ntnu.no>
Date: Thu, 4 May 2023 12:26:10 +0200
Subject: [PATCH] ukemeny

---
 package-lock.json                 | 730 +++++++++++++++++++++++++++++-
 package.json                      |   4 +-
 public/dish-placeholder.png       | Bin 0 -> 11597 bytes
 src/components/Navbar.vue         |   4 +-
 src/components/RecipeCard.vue     |  89 ++++
 src/router/index.js               |  10 +-
 src/style.scss                    |   2 +-
 src/views/FridgeView.vue          |  13 +-
 src/views/PlannerView.vue         |  29 ++
 src/views/ProfileCreationView.vue |  31 +-
 10 files changed, 864 insertions(+), 48 deletions(-)
 create mode 100644 public/dish-placeholder.png
 create mode 100644 src/components/RecipeCard.vue
 create mode 100644 src/views/PlannerView.vue

diff --git a/package-lock.json b/package-lock.json
index f2a8890..1a0fb35 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -19,6 +19,7 @@
       },
       "devDependencies": {
         "@vitejs/plugin-vue": "^4.0.0",
+        "@vitest/coverage-c8": "^0.30.1",
         "@vue/test-utils": "^2.2.6",
         "cypress": "^12.0.2",
         "jsdom": "^20.0.3",
@@ -26,7 +27,7 @@
         "sass-loader": "^13.2.2",
         "start-server-and-test": "^1.15.2",
         "vite": "^4.0.0",
-        "vitest": "^0.25.6"
+        "vitest": "^0.30.1"
       }
     },
     "node_modules/@babel/code-frame": {
@@ -137,6 +138,12 @@
         "node": ">=6.0.0"
       }
     },
+    "node_modules/@bcoe/v8-coverage": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
+      "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
+      "dev": true
+    },
     "node_modules/@colors/colors": {
       "version": "1.5.0",
       "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
@@ -587,6 +594,15 @@
         "vue": ">=3"
       }
     },
+    "node_modules/@istanbuljs/schema": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
+      "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/@jridgewell/gen-mapping": {
       "version": "0.3.3",
       "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
@@ -607,7 +623,6 @@
       "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
       "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
       "dev": true,
-      "peer": true,
       "engines": {
         "node": ">=6.0.0"
       }
@@ -637,15 +652,13 @@
       "version": "1.4.15",
       "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
       "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
-      "dev": true,
-      "peer": true
+      "dev": true
     },
     "node_modules/@jridgewell/trace-mapping": {
       "version": "0.3.18",
       "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
       "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
       "dev": true,
-      "peer": true,
       "dependencies": {
         "@jridgewell/resolve-uri": "3.1.0",
         "@jridgewell/sourcemap-codec": "1.4.14"
@@ -655,8 +668,7 @@
       "version": "1.4.14",
       "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
       "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
-      "dev": true,
-      "peer": true
+      "dev": true
     },
     "node_modules/@npmcli/fs": {
       "version": "2.1.2",
@@ -798,6 +810,12 @@
       "dev": true,
       "peer": true
     },
+    "node_modules/@types/istanbul-lib-coverage": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
+      "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==",
+      "dev": true
+    },
     "node_modules/@types/json-schema": {
       "version": "7.0.11",
       "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
@@ -858,6 +876,104 @@
         "vue": "^3.2.25"
       }
     },
+    "node_modules/@vitest/coverage-c8": {
+      "version": "0.30.1",
+      "resolved": "https://registry.npmjs.org/@vitest/coverage-c8/-/coverage-c8-0.30.1.tgz",
+      "integrity": "sha512-/Wa3dtSuckpdngAmiCwowaEXXgJkqPrtfvrs9HTB9QoEfNbZWPu4E4cjEn4lJZb4qcGf4fxFtUA2f9DnDNAzBA==",
+      "dev": true,
+      "dependencies": {
+        "c8": "^7.13.0",
+        "picocolors": "^1.0.0",
+        "std-env": "^3.3.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "vitest": ">=0.30.0 <1"
+      }
+    },
+    "node_modules/@vitest/expect": {
+      "version": "0.30.1",
+      "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.30.1.tgz",
+      "integrity": "sha512-c3kbEtN8XXJSeN81iDGq29bUzSjQhjES2WR3aColsS4lPGbivwLtas4DNUe0jD9gg/FYGIteqOenfU95EFituw==",
+      "dev": true,
+      "dependencies": {
+        "@vitest/spy": "0.30.1",
+        "@vitest/utils": "0.30.1",
+        "chai": "^4.3.7"
+      }
+    },
+    "node_modules/@vitest/runner": {
+      "version": "0.30.1",
+      "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.30.1.tgz",
+      "integrity": "sha512-W62kT/8i0TF1UBCNMRtRMOBWJKRnNyv9RrjIgdUryEe0wNpGZvvwPDLuzYdxvgSckzjp54DSpv1xUbv4BQ0qVA==",
+      "dev": true,
+      "dependencies": {
+        "@vitest/utils": "0.30.1",
+        "concordance": "^5.0.4",
+        "p-limit": "^4.0.0",
+        "pathe": "^1.1.0"
+      }
+    },
+    "node_modules/@vitest/runner/node_modules/p-limit": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz",
+      "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==",
+      "dev": true,
+      "dependencies": {
+        "yocto-queue": "^1.0.0"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@vitest/snapshot": {
+      "version": "0.30.1",
+      "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.30.1.tgz",
+      "integrity": "sha512-fJZqKrE99zo27uoZA/azgWyWbFvM1rw2APS05yB0JaLwUIg9aUtvvnBf4q7JWhEcAHmSwbrxKFgyBUga6tq9Tw==",
+      "dev": true,
+      "dependencies": {
+        "magic-string": "^0.30.0",
+        "pathe": "^1.1.0",
+        "pretty-format": "^27.5.1"
+      }
+    },
+    "node_modules/@vitest/snapshot/node_modules/magic-string": {
+      "version": "0.30.0",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz",
+      "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.4.13"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@vitest/spy": {
+      "version": "0.30.1",
+      "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.30.1.tgz",
+      "integrity": "sha512-YfJeIf37GvTZe04ZKxzJfnNNuNSmTEGnla2OdL60C8od16f3zOfv9q9K0nNii0NfjDJRt/CVN/POuY5/zTS+BA==",
+      "dev": true,
+      "dependencies": {
+        "tinyspy": "^2.1.0"
+      }
+    },
+    "node_modules/@vitest/utils": {
+      "version": "0.30.1",
+      "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.30.1.tgz",
+      "integrity": "sha512-/c8Xv2zUVc+rnNt84QF0Y0zkfxnaGhp87K2dYJMLtLOIckPzuxLVzAtFCicGFdB4NeBHNzTRr1tNn7rCtQcWFA==",
+      "dev": true,
+      "dependencies": {
+        "concordance": "^5.0.4",
+        "loupe": "^2.3.6",
+        "pretty-format": "^27.5.1"
+      }
+    },
     "node_modules/@vue/compiler-core": {
       "version": "3.2.47",
       "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.47.tgz",
@@ -1551,6 +1667,12 @@
       "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
       "dev": true
     },
+    "node_modules/blueimp-md5": {
+      "version": "2.19.0",
+      "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz",
+      "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==",
+      "dev": true
+    },
     "node_modules/brace-expansion": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
@@ -1640,6 +1762,143 @@
       "dev": true,
       "peer": true
     },
+    "node_modules/c8": {
+      "version": "7.13.0",
+      "resolved": "https://registry.npmjs.org/c8/-/c8-7.13.0.tgz",
+      "integrity": "sha512-/NL4hQTv1gBL6J6ei80zu3IiTrmePDKXKXOTLpHvcIWZTVYQlDhVWjjWvkhICylE8EwwnMVzDZugCvdx0/DIIA==",
+      "dev": true,
+      "dependencies": {
+        "@bcoe/v8-coverage": "^0.2.3",
+        "@istanbuljs/schema": "^0.1.3",
+        "find-up": "^5.0.0",
+        "foreground-child": "^2.0.0",
+        "istanbul-lib-coverage": "^3.2.0",
+        "istanbul-lib-report": "^3.0.0",
+        "istanbul-reports": "^3.1.4",
+        "rimraf": "^3.0.2",
+        "test-exclude": "^6.0.0",
+        "v8-to-istanbul": "^9.0.0",
+        "yargs": "^16.2.0",
+        "yargs-parser": "^20.2.9"
+      },
+      "bin": {
+        "c8": "bin/c8.js"
+      },
+      "engines": {
+        "node": ">=10.12.0"
+      }
+    },
+    "node_modules/c8/node_modules/cliui": {
+      "version": "7.0.4",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+      "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+      "dev": true,
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.0",
+        "wrap-ansi": "^7.0.0"
+      }
+    },
+    "node_modules/c8/node_modules/find-up": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+      "dev": true,
+      "dependencies": {
+        "locate-path": "^6.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/c8/node_modules/locate-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+      "dev": true,
+      "dependencies": {
+        "p-locate": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/c8/node_modules/p-limit": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+      "dev": true,
+      "dependencies": {
+        "yocto-queue": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/c8/node_modules/p-locate": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+      "dev": true,
+      "dependencies": {
+        "p-limit": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/c8/node_modules/yargs": {
+      "version": "16.2.0",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+      "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+      "dev": true,
+      "dependencies": {
+        "cliui": "^7.0.2",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.0",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^20.2.2"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/c8/node_modules/yocto-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/cac": {
+      "version": "6.7.14",
+      "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
+      "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/cacache": {
       "version": "16.1.3",
       "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz",
@@ -2012,6 +2271,25 @@
       "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
       "dev": true
     },
+    "node_modules/concordance": {
+      "version": "5.0.4",
+      "resolved": "https://registry.npmjs.org/concordance/-/concordance-5.0.4.tgz",
+      "integrity": "sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==",
+      "dev": true,
+      "dependencies": {
+        "date-time": "^3.1.0",
+        "esutils": "^2.0.3",
+        "fast-diff": "^1.2.0",
+        "js-string-escape": "^1.0.1",
+        "lodash": "^4.17.15",
+        "md5-hex": "^3.0.1",
+        "semver": "^7.3.2",
+        "well-known-symbols": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10.18.0 <11 || >=12.14.0 <13 || >=14"
+      }
+    },
     "node_modules/config-chain": {
       "version": "1.1.13",
       "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
@@ -2034,6 +2312,12 @@
       "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
       "dev": true
     },
+    "node_modules/convert-source-map": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+      "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+      "dev": true
+    },
     "node_modules/core-util-is": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@@ -2166,6 +2450,18 @@
         "node": ">=12"
       }
     },
+    "node_modules/date-time": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz",
+      "integrity": "sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==",
+      "dev": true,
+      "dependencies": {
+        "time-zone": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/dayjs": {
       "version": "1.11.7",
       "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz",
@@ -2688,6 +2984,12 @@
       "dev": true,
       "peer": true
     },
+    "node_modules/fast-diff": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
+      "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
+      "dev": true
+    },
     "node_modules/fast-json-stable-stringify": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@@ -2769,6 +3071,19 @@
         }
       }
     },
+    "node_modules/foreground-child": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz",
+      "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==",
+      "dev": true,
+      "dependencies": {
+        "cross-spawn": "^7.0.0",
+        "signal-exit": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
     "node_modules/forever-agent": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
@@ -3159,6 +3474,12 @@
         "node": ">=12"
       }
     },
+    "node_modules/html-escaper": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+      "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+      "dev": true
+    },
     "node_modules/http-cache-semantics": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
@@ -3487,6 +3808,54 @@
       "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
       "dev": true
     },
+    "node_modules/istanbul-lib-coverage": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
+      "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/istanbul-lib-report": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
+      "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
+      "dev": true,
+      "dependencies": {
+        "istanbul-lib-coverage": "^3.0.0",
+        "make-dir": "^3.0.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/istanbul-lib-report/node_modules/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,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/istanbul-reports": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz",
+      "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==",
+      "dev": true,
+      "dependencies": {
+        "html-escaper": "^2.0.0",
+        "istanbul-lib-report": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/jest-worker": {
       "version": "27.5.1",
       "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
@@ -3541,6 +3910,15 @@
         "node": ">=10"
       }
     },
+    "node_modules/js-string-escape": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz",
+      "integrity": "sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
     "node_modules/js-tokens": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -3661,6 +4039,12 @@
       "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
       "dev": true
     },
+    "node_modules/jsonc-parser": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
+      "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==",
+      "dev": true
+    },
     "node_modules/jsonfile": {
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
@@ -3904,6 +4288,30 @@
         "sourcemap-codec": "^1.4.8"
       }
     },
+    "node_modules/make-dir": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+      "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+      "dev": true,
+      "dependencies": {
+        "semver": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/make-dir/node_modules/semver": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+      "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
     "node_modules/make-fetch-happen": {
       "version": "10.2.1",
       "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz",
@@ -3958,6 +4366,18 @@
       "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==",
       "dev": true
     },
+    "node_modules/md5-hex": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-3.0.1.tgz",
+      "integrity": "sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==",
+      "dev": true,
+      "dependencies": {
+        "blueimp-md5": "^2.10.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/meow": {
       "version": "9.0.0",
       "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz",
@@ -4190,6 +4610,18 @@
         "node": ">=10"
       }
     },
+    "node_modules/mlly": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.2.0.tgz",
+      "integrity": "sha512-+c7A3CV0KGdKcylsI6khWyts/CYrGTrRVo4R/I7u/cUsy0Conxa6LUhiEzVKIw14lc2L5aiO4+SeVe4TeGRKww==",
+      "dev": true,
+      "dependencies": {
+        "acorn": "^8.8.2",
+        "pathe": "^1.1.0",
+        "pkg-types": "^1.0.2",
+        "ufo": "^1.1.1"
+      }
+    },
     "node_modules/ms": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -4816,6 +5248,12 @@
       "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
       "dev": true
     },
+    "node_modules/pathe": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.0.tgz",
+      "integrity": "sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==",
+      "dev": true
+    },
     "node_modules/pathval": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
@@ -4929,6 +5367,17 @@
         }
       }
     },
+    "node_modules/pkg-types": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.2.tgz",
+      "integrity": "sha512-hM58GKXOcj8WTqUXnsQyJYXdeAPbythQgEF3nTcEo+nkD49chjQ9IKm/QJy9xf6JakXptz86h7ecP2024rrLaQ==",
+      "dev": true,
+      "dependencies": {
+        "jsonc-parser": "^3.2.0",
+        "mlly": "^1.1.1",
+        "pathe": "^1.1.0"
+      }
+    },
     "node_modules/postcss": {
       "version": "8.4.22",
       "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.22.tgz",
@@ -4977,6 +5426,32 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/pretty-format": {
+      "version": "27.5.1",
+      "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
+      "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
+      "dev": true,
+      "dependencies": {
+        "ansi-regex": "^5.0.1",
+        "ansi-styles": "^5.0.0",
+        "react-is": "^17.0.1"
+      },
+      "engines": {
+        "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+      }
+    },
+    "node_modules/pretty-format/node_modules/ansi-styles": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+      "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
     "node_modules/process-nextick-args": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@@ -5100,6 +5575,12 @@
         "safe-buffer": "^5.1.0"
       }
     },
+    "node_modules/react-is": {
+      "version": "17.0.2",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+      "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
+      "dev": true
+    },
     "node_modules/read-pkg": {
       "version": "5.2.0",
       "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
@@ -5640,6 +6121,12 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/siginfo": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+      "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+      "dev": true
+    },
     "node_modules/sigmund": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
@@ -5818,6 +6305,12 @@
         "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
       }
     },
+    "node_modules/stackback": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+      "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+      "dev": true
+    },
     "node_modules/start-server-and-test": {
       "version": "1.15.4",
       "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-1.15.4.tgz",
@@ -5886,6 +6379,12 @@
         "node": ">=10.17.0"
       }
     },
+    "node_modules/std-env": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.3.2.tgz",
+      "integrity": "sha512-uUZI65yrV2Qva5gqE0+A7uVAvO40iPo6jGhs7s8keRfHCmtg+uB2X6EiLGCI9IgL1J17xGhvoOqSz79lzICPTA==",
+      "dev": true
+    },
     "node_modules/stdout-stream": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz",
@@ -6138,6 +6637,62 @@
       "dev": true,
       "peer": true
     },
+    "node_modules/test-exclude": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
+      "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
+      "dev": true,
+      "dependencies": {
+        "@istanbuljs/schema": "^0.1.2",
+        "glob": "^7.1.4",
+        "minimatch": "^3.0.4"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/test-exclude/node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/test-exclude/node_modules/glob": {
+      "version": "7.2.3",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+      "dev": true,
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.1.1",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/test-exclude/node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
     "node_modules/throttleit": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz",
@@ -6150,6 +6705,15 @@
       "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
       "dev": true
     },
+    "node_modules/time-zone": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz",
+      "integrity": "sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
     "node_modules/tinybench": {
       "version": "2.4.0",
       "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.4.0.tgz",
@@ -6157,18 +6721,18 @@
       "dev": true
     },
     "node_modules/tinypool": {
-      "version": "0.3.1",
-      "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.3.1.tgz",
-      "integrity": "sha512-zLA1ZXlstbU2rlpA4CIeVaqvWq41MTWqLY3FfsAXgC8+f7Pk7zroaJQxDgxn1xNudKW6Kmj4808rPFShUlIRmQ==",
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.4.0.tgz",
+      "integrity": "sha512-2ksntHOKf893wSAH4z/+JbPpi92esw8Gn9N2deXX+B0EO92hexAVI9GIZZPx7P5aYo5KULfeOSt3kMOmSOy6uA==",
       "dev": true,
       "engines": {
         "node": ">=14.0.0"
       }
     },
     "node_modules/tinyspy": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-1.1.1.tgz",
-      "integrity": "sha512-UVq5AXt/gQlti7oxoIg5oi/9r0WpF7DGEVwXgqWSMmyN16+e3tl5lIvTaOpJ3TAtu5xFzWccFRM4R5NaWHF+4g==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.1.0.tgz",
+      "integrity": "sha512-7eORpyqImoOvkQJCSkL0d0mB4NHHIFAy4b1u8PHdDa7SjGS2njzl6/lyGoZLm+eyYEtlUmFGE0rFj66SWxZgQQ==",
       "dev": true,
       "engines": {
         "node": ">=14.0.0"
@@ -6294,6 +6858,12 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/ufo": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.1.1.tgz",
+      "integrity": "sha512-MvlCc4GHrmZdAllBc0iUDowff36Q9Ndw/UzqmEKyrfSzokTd9ZCy1i+IIk5hrYKkjoYVQyNbrw7/F8XJ2rEwTg==",
+      "dev": true
+    },
     "node_modules/unique-filename": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz",
@@ -6402,6 +6972,20 @@
         "uuid": "dist/bin/uuid"
       }
     },
+    "node_modules/v8-to-istanbul": {
+      "version": "9.1.0",
+      "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz",
+      "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/trace-mapping": "^0.3.12",
+        "@types/istanbul-lib-coverage": "^2.0.1",
+        "convert-source-map": "^1.6.0"
+      },
+      "engines": {
+        "node": ">=10.12.0"
+      }
+    },
     "node_modules/validate-npm-package-license": {
       "version": "3.0.4",
       "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
@@ -6475,32 +7059,67 @@
         }
       }
     },
+    "node_modules/vite-node": {
+      "version": "0.30.1",
+      "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.30.1.tgz",
+      "integrity": "sha512-vTikpU/J7e6LU/8iM3dzBo8ZhEiKZEKRznEMm+mJh95XhWaPrJQraT/QsT2NWmuEf+zgAoMe64PKT7hfZ1Njmg==",
+      "dev": true,
+      "dependencies": {
+        "cac": "^6.7.14",
+        "debug": "^4.3.4",
+        "mlly": "^1.2.0",
+        "pathe": "^1.1.0",
+        "picocolors": "^1.0.0",
+        "vite": "^3.0.0 || ^4.0.0"
+      },
+      "bin": {
+        "vite-node": "vite-node.mjs"
+      },
+      "engines": {
+        "node": ">=v14.18.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
     "node_modules/vitest": {
-      "version": "0.25.8",
-      "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.25.8.tgz",
-      "integrity": "sha512-X75TApG2wZTJn299E/TIYevr4E9/nBo1sUtZzn0Ci5oK8qnpZAZyhwg0qCeMSakGIWtc6oRwcQFyFfW14aOFWg==",
+      "version": "0.30.1",
+      "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.30.1.tgz",
+      "integrity": "sha512-y35WTrSTlTxfMLttgQk4rHcaDkbHQwDP++SNwPb+7H8yb13Q3cu2EixrtHzF27iZ8v0XCciSsLg00RkPAzB/aA==",
       "dev": true,
       "dependencies": {
         "@types/chai": "^4.3.4",
         "@types/chai-subset": "^1.3.3",
         "@types/node": "*",
-        "acorn": "^8.8.1",
+        "@vitest/expect": "0.30.1",
+        "@vitest/runner": "0.30.1",
+        "@vitest/snapshot": "0.30.1",
+        "@vitest/spy": "0.30.1",
+        "@vitest/utils": "0.30.1",
+        "acorn": "^8.8.2",
         "acorn-walk": "^8.2.0",
+        "cac": "^6.7.14",
         "chai": "^4.3.7",
+        "concordance": "^5.0.4",
         "debug": "^4.3.4",
-        "local-pkg": "^0.4.2",
+        "local-pkg": "^0.4.3",
+        "magic-string": "^0.30.0",
+        "pathe": "^1.1.0",
+        "picocolors": "^1.0.0",
         "source-map": "^0.6.1",
-        "strip-literal": "^1.0.0",
-        "tinybench": "^2.3.1",
-        "tinypool": "^0.3.0",
-        "tinyspy": "^1.0.2",
-        "vite": "^3.0.0 || ^4.0.0"
+        "std-env": "^3.3.2",
+        "strip-literal": "^1.0.1",
+        "tinybench": "^2.4.0",
+        "tinypool": "^0.4.0",
+        "vite": "^3.0.0 || ^4.0.0",
+        "vite-node": "0.30.1",
+        "why-is-node-running": "^2.2.2"
       },
       "bin": {
         "vitest": "vitest.mjs"
       },
       "engines": {
-        "node": ">=v14.16.0"
+        "node": ">=v14.18.0"
       },
       "funding": {
         "url": "https://github.com/sponsors/antfu"
@@ -6510,7 +7129,10 @@
         "@vitest/browser": "*",
         "@vitest/ui": "*",
         "happy-dom": "*",
-        "jsdom": "*"
+        "jsdom": "*",
+        "playwright": "*",
+        "safaridriver": "*",
+        "webdriverio": "*"
       },
       "peerDependenciesMeta": {
         "@edge-runtime/vm": {
@@ -6527,9 +7149,30 @@
         },
         "jsdom": {
           "optional": true
+        },
+        "playwright": {
+          "optional": true
+        },
+        "safaridriver": {
+          "optional": true
+        },
+        "webdriverio": {
+          "optional": true
         }
       }
     },
+    "node_modules/vitest/node_modules/magic-string": {
+      "version": "0.30.0",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz",
+      "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.4.13"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/vue": {
       "version": "3.2.47",
       "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.47.tgz",
@@ -6668,6 +7311,15 @@
         "node": ">=10.13.0"
       }
     },
+    "node_modules/well-known-symbols": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/well-known-symbols/-/well-known-symbols-2.0.0.tgz",
+      "integrity": "sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/whatwg-encoding": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz",
@@ -6717,6 +7369,22 @@
         "node": ">= 8"
       }
     },
+    "node_modules/why-is-node-running": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz",
+      "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==",
+      "dev": true,
+      "dependencies": {
+        "siginfo": "^2.0.0",
+        "stackback": "0.0.2"
+      },
+      "bin": {
+        "why-is-node-running": "cli.js"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/wide-align": {
       "version": "1.1.5",
       "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
@@ -6854,6 +7522,18 @@
         "buffer-crc32": "~0.2.3",
         "fd-slicer": "~1.1.0"
       }
+    },
+    "node_modules/yocto-queue": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz",
+      "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==",
+      "dev": true,
+      "engines": {
+        "node": ">=12.20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
     }
   }
 }
diff --git a/package.json b/package.json
index 7a96571..720c8cb 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,7 @@
     "build": "vite build",
     "preview": "vite preview",
     "test:unit": "vitest --environment jsdom --root src/",
+    "test:unit-coverage": "vitest --environment jsdom --root src/ --coverage",
     "test:e2e": "start-server-and-test preview :4173 'cypress run --e2e'",
     "test:e2e:dev": "start-server-and-test 'vite dev --port 4173' :4173 'cypress run --e2e'"
   },
@@ -22,6 +23,7 @@
   },
   "devDependencies": {
     "@vitejs/plugin-vue": "^4.0.0",
+    "@vitest/coverage-c8": "^0.30.1",
     "@vue/test-utils": "^2.2.6",
     "cypress": "^12.0.2",
     "jsdom": "^20.0.3",
@@ -29,6 +31,6 @@
     "sass-loader": "^13.2.2",
     "start-server-and-test": "^1.15.2",
     "vite": "^4.0.0",
-    "vitest": "^0.25.6"
+    "vitest": "^0.30.1"
   }
 }
diff --git a/public/dish-placeholder.png b/public/dish-placeholder.png
new file mode 100644
index 0000000000000000000000000000000000000000..a549281ea41a5bf6251fc52e5c58900bb43508da
GIT binary patch
literal 11597
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

literal 0
HcmV?d00001

diff --git a/src/components/Navbar.vue b/src/components/Navbar.vue
index 10d7e10..b0e13c3 100644
--- a/src/components/Navbar.vue
+++ b/src/components/Navbar.vue
@@ -13,7 +13,7 @@
                     </RouterLink>
                  </li>
                 <li>
-                    <RouterLink :to="'/'" :aria-label="'link to week menu'">
+                    <RouterLink to="/planner" aria-label="link to week menu">
                         <Icon icon="ic:baseline-calendar-month" :color="iconColor" :style="{ fontSize: iconSize }"/>
                     </RouterLink>
                 </li>
@@ -73,7 +73,7 @@ nav {
     width: 100%;
     background-color: base.$green;
     margin:0;
-    qpadding:5px;
+    padding:5px;
     align-items: center;
 
 }
diff --git a/src/components/RecipeCard.vue b/src/components/RecipeCard.vue
new file mode 100644
index 0000000..04aa4f2
--- /dev/null
+++ b/src/components/RecipeCard.vue
@@ -0,0 +1,89 @@
+<script>
+import { Icon } from '@iconify/vue';
+
+export default {
+    props: ['recipe'],
+    components: {
+        Icon
+    }
+}
+</script>
+
+<template>
+    <div class="recipe_card">
+        <img class="recipe_img" src="/dish-placeholder.png" :alt="`Bilde av {recipe.title}`">
+        <div class="recipe_details">
+            <div class="title">
+                <h2>{{ recipe.title }}</h2>
+                <p>{{ recipe.description }}</p>
+            </div>
+            <div class="time">
+                <Icon icon="mdi:timer" :style="{ fontSize: '2rem' }" />
+                {{ recipe.time }}
+            </div>
+        </div>
+    </div>
+</template>
+
+<style scoped lang="scss">
+@import "../style.scss";
+
+.recipe_card {
+    display: flex;
+    flex-direction: column;
+    align-content: center;
+
+    height: 15rem;
+    max-width: 30rem;
+    gap: .5rem;
+    margin-inline: .4rem;
+
+    border: 1px solid black;
+    border-radius: 1rem;
+    overflow: hidden;
+
+    box-shadow: 2px 2px $green;
+
+    transition: all ease 150ms;
+
+    &:hover {
+        box-shadow: 4px 4px $green;
+    }
+
+    img {
+        object-fit: cover;
+        height: 10rem;
+    }
+}
+
+.recipe_details {
+    margin-inline: 1em;
+
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    
+    .title {
+        p {        
+            overflow: hidden;
+            white-space: nowrap;
+            text-overflow: "...";
+
+            max-width: 20em;
+        }
+    }
+
+    .time {
+        width: 4em;
+
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        align-items: center;
+
+        p {
+            width: 100%;
+        }
+    }
+}
+</style>
\ No newline at end of file
diff --git a/src/router/index.js b/src/router/index.js
index aa8efde..8d1e93c 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -6,6 +6,7 @@ import LoginView from '../views/LoginView.vue'
 import SelectProfileView from '../views/SelectProfileView.vue'
 import ProfileCreationView from '../views/ProfileCreationView.vue'
 import RegisterAccountView from '../views/RegisterAccountView.vue'
+import PlannerView from '../views/PlannerView.vue'
 import PinCodeView from "@/views/PinCodeView.vue";
 import FridgeView from "@/views/FridgeView.vue";
 import RecipeView from "@/views/RecipeView.vue";
@@ -50,6 +51,11 @@ const router = createRouter({
       name: 'myFridge',
       component: FridgeView
     },
+    {
+      path: '/planner',
+      name: 'planner',
+      component: PlannerView
+    },
     {
       path: '/recipe/:id',
       name: 'recipe',
@@ -68,8 +74,8 @@ const router = createRouter({
     {
       path: '/:catchAll(.*)',
       name: "404 Page not found" ,
-      component: MissingPage }
-
+      component: MissingPage 
+    }
   ]
 })
 
diff --git a/src/style.scss b/src/style.scss
index 9cc6bdc..5ac9953 100644
--- a/src/style.scss
+++ b/src/style.scss
@@ -46,4 +46,4 @@ $phone-min : 360px;
   width: 100%;
   margin: 0;
   height: 100%;
-}
\ No newline at end of file
+}
diff --git a/src/views/FridgeView.vue b/src/views/FridgeView.vue
index 6ee345d..0025e63 100644
--- a/src/views/FridgeView.vue
+++ b/src/views/FridgeView.vue
@@ -67,10 +67,17 @@ export default {
 
   },
   async mounted() {
-      await API.getFridgeItems()
-      if(this.fridgeItems.length===0){
+      API.getFridgeItems()
+      .then((fridgeItems) => {
+        this.fridgeItems = fridgeItems;
+        
+        if(this.fridgeItems.length===0){
           this.fridgeMsg="Kjøleskapet ditt er tomt."
-      }
+        }
+      })
+      .catch(() => {
+        this.fridgeMsg = "Kjøleskapet ditt er tomt."
+      })
   }
 }
 </script>
diff --git a/src/views/PlannerView.vue b/src/views/PlannerView.vue
new file mode 100644
index 0000000..459ebb9
--- /dev/null
+++ b/src/views/PlannerView.vue
@@ -0,0 +1,29 @@
+<script>
+import RecipeCard from '../components/RecipeCard.vue';
+export default {
+    components: { RecipeCard }
+}
+</script>
+
+<template>
+    <div class="planner_container">
+        <h1>Denne uken</h1>
+        <div class="recipe_list">
+            <RecipeCard :recipe="{ id: 1, title: 'Kjøttkaker', description: 'Skikkelig gode kjøttkaker', time: '15 min' }" />
+            <RecipeCard :recipe="{ id: 1, title: 'Kjøttkaker', description: 'Skikkelig gode kjøttkaker med veldig veldig lang beskrivelse', time: '15 min' }" />
+
+        </div>
+    </div>
+</template>
+
+<style>
+h1 {
+    padding-left: 0.5em;
+}
+
+.recipe_list {
+    display: flex;
+    flex-direction: column;
+    gap: 1rem;
+}
+</style>
\ No newline at end of file
diff --git a/src/views/ProfileCreationView.vue b/src/views/ProfileCreationView.vue
index 560f3c5..315c9cb 100644
--- a/src/views/ProfileCreationView.vue
+++ b/src/views/ProfileCreationView.vue
@@ -18,18 +18,22 @@ export default {
     methods: {
         async submit() {
             await API.addProfile(this.profile)
-            .then((profile) => {
-                let id = profile.id;
-
-                let image = document.getElementById("profile_img").files[0];
-                
-                API.uploadProfileImage(image, id)
-                .then((updatedProfile) => {
-                    this.authStore.profile = updatedProfile;                     
-                    router.push("/");
-                });
-
-            })
+                .then((profile) => {
+                    let id = profile.id;
+
+                    let image = document.getElementById("profile_img").files[0];
+
+                    if (typeof image === 'undefined') {
+                        this.authStore.profile = profile;
+                        router.push("/");
+                    }
+                    API.uploadProfileImage(image, id)
+                        .then((updatedProfile) => {
+                            this.authStore.profile = updatedProfile;
+                            router.push("/");
+                        });
+
+                })
         },
         updateImg() {
             let file = document.getElementById("profile_img").files[0];
@@ -59,8 +63,7 @@ export default {
                     </p>
                 </div>
             </label>
-            <input @change="updateImg" type="file"
-                accept=".jpeg, .jpg" id="profile_img" name="profile_img">
+            <input @change="updateImg" type="file" accept=".jpeg, .jpg" id="profile_img" name="profile_img">
             <label for="name">Navn</label>
             <input name="name" type="text" v-model="profile.name">
             <div class="check_container">
-- 
GitLab