+
-
@@ -127,7 +135,7 @@ import Modal from "bootstrap/js/dist/modal";
import "@h2d2/shopicons/es/filled/plus";
import "@h2d2/shopicons/es/filled/edit";
import LabelAndValue from "./LabelAndValue.vue";
-import TargetChargePlanMinimal from "./TargetChargePlanMinimal.vue";
+import TargetChargePlan from "./TargetChargePlan.vue";
import api from "../api";
import formatter from "../mixins/formatter";
@@ -137,7 +145,7 @@ const LAST_TARGET_TIME_KEY = "last_target_time";
export default {
name: "TargetCharge",
- components: { LabelAndValue, TargetChargePlanMinimal },
+ components: { LabelAndValue, TargetChargePlan },
mixins: [formatter],
props: {
id: [String, Number],
@@ -154,6 +162,7 @@ export default {
selectedDay: null,
selectedTime: null,
plan: {},
+ tariff: {},
modal: null,
isModalVisible: false,
};
@@ -162,9 +171,19 @@ export default {
targetChargeEnabled: function () {
return this.targetTime;
},
- selectedTargetTimeValid: function () {
+ timeInThePast: function () {
const now = new Date();
- return now < this.selectedTargetTime;
+ return now >= this.selectedTargetTime;
+ },
+ timeTooFarInTheFuture: function () {
+ if (this.tariff?.rates) {
+ const lastRate = this.tariff.rates[this.tariff.rates.length - 1];
+ if (lastRate.end) {
+ const end = new Date(lastRate.end);
+ return this.selectedTargetTime >= end;
+ }
+ }
+ return false;
},
selectedTargetTime: function () {
return new Date(`${this.selectedDay}T${this.selectedTime || "00:00"}`);
@@ -175,6 +194,12 @@ export default {
targetEnergyFormatted: function () {
return this.fmtKWh(this.targetEnergy * 1e3, true, true, 1);
},
+ targetChargePlanProps: function () {
+ const targetTime = this.selectedTargetTime;
+ const { rates } = this.tariff;
+ const { duration, unit, plan } = this.plan;
+ return rates ? { duration, rates, plan, unit, targetTime } : null;
+ },
},
watch: {
targetTimeLabel: function () {
@@ -219,14 +244,17 @@ export default {
updatePlan: async function () {
if (
this.isModalVisible &&
- this.selectedTargetTimeValid &&
+ !this.timeInThePast &&
(this.targetEnergy || this.targetSoc)
) {
try {
- const response = await api.get(`/loadpoints/${this.id}/target/plan`, {
+ const opts = {
params: { targetTime: this.selectedTargetTime },
- });
- this.plan = response.data.result;
+ };
+ this.plan = (
+ await api.get(`/loadpoints/${this.id}/target/plan`, opts)
+ ).data.result;
+ this.tariff = (await api.get(`/tariff/planner`)).data.result;
} catch (e) {
console.error(e);
}
@@ -323,4 +351,12 @@ export default {
.value:hover {
color: var(--bs-color-white);
}
+@media (min-width: 992px) {
+ .date-selection {
+ width: 370px;
+ }
+}
+.time-selection {
+ flex-basis: 200px;
+}
diff --git a/assets/js/components/TargetChargePlan.story.vue b/assets/js/components/TargetChargePlan.story.vue
new file mode 100644
index 0000000000..d6aeba97b3
--- /dev/null
+++ b/assets/js/components/TargetChargePlan.story.vue
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/js/components/TargetChargePlan.test.js b/assets/js/components/TargetChargePlan.test.js
new file mode 100644
index 0000000000..eb8cc10fc1
--- /dev/null
+++ b/assets/js/components/TargetChargePlan.test.js
@@ -0,0 +1,136 @@
+import { mount, config } from "@vue/test-utils";
+import { beforeAll, describe, expect, test } from "vitest";
+import TargetChargePlan from "./TargetChargePlan.vue";
+
+config.global.mocks["$i18n"] = { locale: "de-DE" };
+config.global.mocks["$t"] = (a) => a;
+
+describe("basics", () => {
+ const DATE_START = new Date("2023-01-11T11:00:00+01:00");
+ const DATE_TARGET = new Date("2023-01-11T13:00:00+01:00");
+ const TARIFF_FIXED = [
+ {
+ start: "2023-01-11T11:00:00+01:00",
+ end: "2023-01-22T00:00:00+01:00",
+ price: 0.4,
+ },
+ ];
+ const PLAN = [
+ {
+ start: "2023-01-11T12:00:00+01:00",
+ end: "2023-01-11T13:00:00+01:00",
+ },
+ ];
+
+ const wrapper = mount(TargetChargePlan, {
+ props: {
+ plan: PLAN,
+ targetTime: DATE_TARGET,
+ rates: TARIFF_FIXED,
+ },
+ });
+ wrapper.setData({ startTime: DATE_START });
+
+ let result = null;
+
+ beforeAll(() => {
+ result = wrapper.vm.slots;
+ });
+
+ test("should return 42 slots", () => {
+ expect(result.length).eq(42);
+ });
+
+ test("slots should be an hour apart", () => {
+ expect(result[0].startHour).eq(11);
+ expect(result[0].endHour).eq(12);
+ expect(result[0].day).eq("Mi");
+
+ expect(result[1].startHour).eq(12);
+ expect(result[1].endHour).eq(13);
+ expect(result[1].day).eq("Mi");
+
+ expect(result[11].startHour).eq(22);
+ expect(result[11].endHour).eq(23);
+ expect(result[11].day).eq("Mi");
+
+ expect(result[24].startHour).eq(11);
+ expect(result[24].endHour).eq(12);
+ expect(result[24].day).eq("Do");
+ });
+
+ test("slots after target should be toLate", () => {
+ expect(result[0].toLate).eq(false);
+ expect(result[1].toLate).eq(false);
+ expect(result[2].toLate).eq(true);
+ expect(result[3].toLate).eq(true);
+ });
+
+ test("slots are marked if charging is happening in them", () => {
+ expect(result[0].charging).eq(false);
+ expect(result[1].charging).eq(true);
+ expect(result[2].charging).eq(false);
+ expect(result[3].charging).eq(false);
+ });
+
+ test("all slots have the same fixed price", () => {
+ result.forEach((slot) => expect(slot.price).eq(0.4));
+ });
+});
+
+describe("zoned tariffs", () => {
+ const DATE_START = new Date("2023-01-11T11:00:00+01:00");
+ const DATE_TARGET = new Date("2023-01-11T16:00:00+01:00");
+ const TARIFF_ZONED = [
+ {
+ start: "2023-01-11T11:00:00+01:00",
+ end: "2023-01-11T12:00:00+01:00",
+ price: 0.2,
+ },
+ {
+ start: "2023-01-11T12:00:00+01:00",
+ end: "2023-01-22T00:00:00+01:00",
+ price: 0.4,
+ },
+ ];
+ const PLAN = [
+ {
+ start: "2023-01-11T11:30:00+01:00",
+ end: "2023-01-11T13:00:00+01:00",
+ },
+ {
+ start: "2023-01-11T14:30:00+01:00",
+ end: "2023-01-11T16:00:00+01:00",
+ },
+ ];
+
+ let result = null;
+
+ const wrapper = mount(TargetChargePlan, {
+ props: {
+ plan: PLAN,
+ targetTime: DATE_TARGET,
+ rates: TARIFF_ZONED,
+ },
+ });
+ wrapper.setData({ startTime: DATE_START });
+
+ beforeAll(() => {
+ result = wrapper.vm.slots;
+ });
+
+ test("handle multiple charging slots", () => {
+ expect(result[0].charging).eq(true);
+ expect(result[1].charging).eq(true);
+ expect(result[2].charging).eq(false);
+ expect(result[3].charging).eq(true);
+ expect(result[4].charging).eq(true);
+ expect(result[5].charging).eq(false);
+ });
+
+ test("first slot is cheap, others are expensive", () => {
+ const [first, ...others] = result;
+ expect(first.price).eq(0.2);
+ others.forEach((slot) => expect(slot.price).eq(0.4));
+ });
+});
diff --git a/assets/js/components/TargetChargePlan.vue b/assets/js/components/TargetChargePlan.vue
new file mode 100644
index 0000000000..7de39a2ed3
--- /dev/null
+++ b/assets/js/components/TargetChargePlan.vue
@@ -0,0 +1,283 @@
+
+
+
+
+
{{ $t("main.targetChargePlan.chargeDuration") }}
+
{{ planDuration }}
+
+
+
+ {{ activeSlotName }}
+ {{ $t("main.targetChargePlan.co2Label") }}
+ {{ $t("main.targetChargePlan.priceLabel") }}
+
+
+ {{ fmtAvgPrice }}
+
+
+
+
+
+
+ ?
+
+
+ {{ slot.startHour }}
+
+ {{ slot.day }}
+
+
+
+
+
+
+
+
+
diff --git a/assets/js/mixins/formatter.js b/assets/js/mixins/formatter.js
index f35eb46d16..974ba58c79 100644
--- a/assets/js/mixins/formatter.js
+++ b/assets/js/mixins/formatter.js
@@ -132,6 +132,11 @@ export default {
minute: "numeric",
}).format(date);
},
+ weekdayShort: function (date) {
+ return new Intl.DateTimeFormat(this.$i18n.locale, {
+ weekday: "short",
+ }).format(date);
+ },
fmtAbsoluteDate: function (date) {
const weekday = this.weekdayPrefix(date);
const hour = new Intl.DateTimeFormat(this.$i18n.locale, {
diff --git a/cmd/demo.yaml b/cmd/demo.yaml
index f01483f16f..362951fa8b 100644
--- a/cmd/demo.yaml
+++ b/cmd/demo.yaml
@@ -233,6 +233,9 @@ tariffs:
grid:
type: fixed
price: 0.399 # EUR/kWh
+ # grid:
+ # type: awattar
+ # region: de
feedin:
type: fixed
price: 0.08 # EUR/kWh
diff --git a/histoire.config.js b/histoire.config.js
index 138339c235..f60de21a97 100644
--- a/histoire.config.js
+++ b/histoire.config.js
@@ -5,4 +5,5 @@ export default defineConfig({
plugins: [HstVue()],
setupFile: "./histoire.setup.js",
viteNodeInlineDeps: [/!axios/],
+ routerMode: "hash",
});
diff --git a/i18n/de.toml b/i18n/de.toml
index 86a2ba427d..f9a9e05fe4 100644
--- a/i18n/de.toml
+++ b/i18n/de.toml
@@ -191,13 +191,22 @@ descriptionEnergy = "Bis wann sollen {targetEnergy} ins Fahrzeug geladen sein?"
today = "heute"
tomorrow = "morgen"
targetIsInThePast = "Wähle einen Zeitpunkt in der Zukunft, Marty."
+targetIsTooFarInTheFuture = "Wir passen den Plan an sobald wir mehr über die Zukunft wissen."
remove = "Entfernen"
activate = "Aktivieren"
+update = "Aktualisieren"
planUnknown = "unbekannt"
planDuration = "Ladezeit"
planPeriodLabel = "Zeitraum"
planPeriodValue = "{start} bis {end}"
+[main.targetChargePlan]
+timeRange = "{day} {range} Uhr"
+chargeDuration = "Ladezeit"
+co2Label = "CO₂-Emission Ø"
+priceLabel = "Energiepreis"
+unknownPrice = "noch unbekannt"
+
[main.targetEnergy]
label = "Ladeziel"
noLimit = "keins"
diff --git a/i18n/en.toml b/i18n/en.toml
index ca19a8d9e5..cbc5d54055 100644
--- a/i18n/en.toml
+++ b/i18n/en.toml
@@ -191,13 +191,22 @@ descriptionEnergy = "Until when should {targetEnergy} be loaded into the vehicle
today = "today"
tomorrow = "tomorrow"
targetIsInThePast = "Pick a time in the future, Marty."
+targetIsTooFarInTheFuture = "We will adjust the plan as soon as we know more about the future."
remove = "Remove"
activate = "Activate"
-planUnknown = "unknown"
+update = "Update"
+planUnknown = "not known yet"
planDuration = "Charging time"
planPeriodLabel = "Period"
planPeriodValue = "{start} to {end}"
+[main.targetChargePlan]
+timeRange = "{day} {range} h"
+chargeDuration = "Charging time"
+co2Label = "CO₂ emission Ø"
+priceLabel = "Energy price"
+unknownPrice = "still unknown"
+
[main.targetEnergy]
label = "Limit"
noLimit = "none"
diff --git a/package-lock.json b/package-lock.json
index 748f7469a8..43be5edf4c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,6 +13,7 @@
"@popperjs/core": "^2.11.5",
"@vitejs/plugin-vue": "^3.0.1",
"@vue/compiler-sfc": "^3.2.30",
+ "@vue/test-utils": "^2.2.7",
"axios": "^1.2.1",
"bootstrap": "^5.2.2",
"canvas-confetti": "^1.4.0",
@@ -39,6 +40,10 @@
"vue-meta": "^3.0.0-alpha.2",
"vue-router": "^4.0.12"
},
+ "devDependencies": {
+ "happy-dom": "^8.1.4",
+ "vitest": "^0.27.1"
+ },
"engines": {
"node": ">=18.0.0",
"npm": ">=8.0.0"
@@ -781,6 +786,21 @@
"node": ">= 10"
}
},
+ "node_modules/@types/chai": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz",
+ "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==",
+ "dev": true
+ },
+ "node_modules/@types/chai-subset": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.3.tgz",
+ "integrity": "sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==",
+ "dev": true,
+ "dependencies": {
+ "@types/chai": "*"
+ }
+ },
"node_modules/@types/flexsearch": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/@types/flexsearch/-/flexsearch-0.7.3.tgz",
@@ -942,6 +962,14 @@
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz",
"integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg=="
},
+ "node_modules/@vue/test-utils": {
+ "version": "2.2.7",
+ "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.2.7.tgz",
+ "integrity": "sha512-BMuoruUFTEqhLoOgsMcgNVMiByYbfHCKGr2C4CPdGtz/affUtDVX5zr1RnPuq0tYSiaqq+Enw5voUpG6JY8Q7g==",
+ "peerDependencies": {
+ "vue": "^3.0.1"
+ }
+ },
"node_modules/abab": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
@@ -1080,6 +1108,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/assertion-error": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -1202,6 +1239,15 @@
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
},
+ "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/call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
@@ -1265,6 +1311,24 @@
"upper-case-first": "^2.0.2"
}
},
+ "node_modules/chai": {
+ "version": "4.3.7",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz",
+ "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==",
+ "dev": true,
+ "dependencies": {
+ "assertion-error": "^1.1.0",
+ "check-error": "^1.0.2",
+ "deep-eql": "^4.1.2",
+ "get-func-name": "^2.0.0",
+ "loupe": "^2.3.1",
+ "pathval": "^1.1.1",
+ "type-detect": "^4.0.5"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -1297,6 +1361,15 @@
"tslib": "^2.0.3"
}
},
+ "node_modules/check-error": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
+ "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/chevrotain": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-4.1.1.tgz",
@@ -1438,6 +1511,12 @@
"node": ">= 8"
}
},
+ "node_modules/css.escape": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
+ "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==",
+ "dev": true
+ },
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@@ -1509,6 +1588,18 @@
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
"integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA=="
},
+ "node_modules/deep-eql": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz",
+ "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==",
+ "dev": true,
+ "dependencies": {
+ "type-detect": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -2930,6 +3021,15 @@
"node": "6.* || 8.* || >= 10.*"
}
},
+ "node_modules/get-func-name": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
+ "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/get-intrinsic": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
@@ -3083,6 +3183,20 @@
"js-yaml": "bin/js-yaml.js"
}
},
+ "node_modules/happy-dom": {
+ "version": "8.1.4",
+ "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-8.1.4.tgz",
+ "integrity": "sha512-mUCzXHhSO6fOQlZwKW6z2f/+rYavKNxNrgY4nJ4dp+r8gTGbTENgMZGfM6eJD0DJPRFF8DFyngXdBF93wF96UA==",
+ "dev": true,
+ "dependencies": {
+ "css.escape": "^1.5.1",
+ "he": "^1.2.0",
+ "node-fetch": "^2.x.x",
+ "webidl-conversions": "^7.0.0",
+ "whatwg-encoding": "^2.0.0",
+ "whatwg-mimetype": "^3.0.0"
+ }
+ },
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@@ -3157,6 +3271,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true,
+ "bin": {
+ "he": "bin/he"
+ }
+ },
"node_modules/header-case": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz",
@@ -3748,6 +3871,18 @@
"uc.micro": "^1.0.1"
}
},
+ "node_modules/local-pkg": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.2.tgz",
+ "integrity": "sha512-mlERgSPrbxU3BP4qBqAvvwlgW4MTg78iwJdGGnv7kibKjWcJksrG3t6LB5lXI93wXRDvG4NpUgJFmTG4T6rdrg==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
"node_modules/locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@@ -3772,6 +3907,15 @@
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
},
+ "node_modules/loupe": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz",
+ "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==",
+ "dev": true,
+ "dependencies": {
+ "get-func-name": "^2.0.0"
+ }
+ },
"node_modules/lower-case": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
@@ -3913,14 +4057,14 @@
}
},
"node_modules/mlly": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.0.0.tgz",
- "integrity": "sha512-QL108Hwt+u9bXdWgOI0dhzZfACovn5Aen4Xvc8Jasd9ouRH4NjnrXEiyP3nVvJo91zPlYjVRckta0Nt2zfoR6g==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.1.0.tgz",
+ "integrity": "sha512-cwzBrBfwGC1gYJyfcy8TcZU1f+dbH/T+TuOhtYP2wLv/Fb51/uV7HJQfBPtEupZ2ORLRU1EKFS/QfS3eo9+kBQ==",
"dependencies": {
"acorn": "^8.8.1",
"pathe": "^1.0.0",
- "pkg-types": "^1.0.0",
- "ufo": "^1.0.0"
+ "pkg-types": "^1.0.1",
+ "ufo": "^1.0.1"
}
},
"node_modules/mlly/node_modules/pathe": {
@@ -3974,6 +4118,48 @@
"tslib": "^2.0.3"
}
},
+ "node_modules/node-fetch": {
+ "version": "2.6.8",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.8.tgz",
+ "integrity": "sha512-RZ6dBYuj8dRSfxpUSu+NsdF1dpPpluJxwOp+6IoDp/sH2QNDSvurYsAa+F1WxY2RjA1iP93xhcsUoYbF2XBqVg==",
+ "dev": true,
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/node-fetch/node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "dev": true
+ },
+ "node_modules/node-fetch/node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+ "dev": true
+ },
+ "node_modules/node-fetch/node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dev": true,
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
"node_modules/node-releases": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz",
@@ -4241,6 +4427,15 @@
"resolved": "https://registry.npmjs.org/pathe/-/pathe-0.2.0.tgz",
"integrity": "sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw=="
},
+ "node_modules/pathval": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
+ "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -4683,6 +4878,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/sirv": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.2.tgz",
@@ -4757,6 +4958,12 @@
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
},
+ "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/statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
@@ -4842,6 +5049,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/strip-literal": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.0.0.tgz",
+ "integrity": "sha512-5o4LsH1lzBzO9UFH63AJ2ad2/S2AVx6NtjOcaz+VTT2h1RiRvbipW72z8M/lxEhcPHDBQwpDrnTF7sXy/7OwCQ==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.8.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
"node_modules/style-mod": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.0.0.tgz",
@@ -4879,6 +5098,12 @@
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="
},
+ "node_modules/tinybench": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.3.1.tgz",
+ "integrity": "sha512-hGYWYBMPr7p4g5IarQE7XhlyWveh1EKhy4wUBS1LrHXCKYgvz+4/jCqgmJqZxxldesn05vccrtME2RLLZNW7iA==",
+ "dev": true
+ },
"node_modules/tinypool": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.1.3.tgz",
@@ -4887,6 +5112,15 @@
"node": ">=14.0.0"
}
},
+ "node_modules/tinyspy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-1.0.2.tgz",
+ "integrity": "sha512-bSGlgwLBYf7PnUsQ6WOc6SJ3pGOcd+d8AA6EUnLDDM0kWEstC1JIlSZA3UNliDXhd9ABoS7hiRBDCu+XP/sf1Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
"node_modules/to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
@@ -4999,6 +5233,15 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/type-detect": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/type-fest": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
@@ -5233,6 +5476,99 @@
"fsevents": "~2.3.2"
}
},
+ "node_modules/vitest": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.27.1.tgz",
+ "integrity": "sha512-1sIpQ1DVFTEn7c1ici1XHcVfdU4nKiBmPtPAtGKJJJLuJjojTv/OHGgcf69P57alM4ty8V4NMv+7Yoi5Cxqx9g==",
+ "dev": true,
+ "dependencies": {
+ "@types/chai": "^4.3.4",
+ "@types/chai-subset": "^1.3.3",
+ "@types/node": "*",
+ "acorn": "^8.8.1",
+ "acorn-walk": "^8.2.0",
+ "cac": "^6.7.14",
+ "chai": "^4.3.7",
+ "debug": "^4.3.4",
+ "local-pkg": "^0.4.2",
+ "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",
+ "vite-node": "0.27.1",
+ "why-is-node-running": "^2.2.2"
+ },
+ "bin": {
+ "vitest": "vitest.mjs"
+ },
+ "engines": {
+ "node": ">=v14.16.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "@edge-runtime/vm": "*",
+ "@vitest/browser": "*",
+ "@vitest/ui": "*",
+ "happy-dom": "*",
+ "jsdom": "*"
+ },
+ "peerDependenciesMeta": {
+ "@edge-runtime/vm": {
+ "optional": true
+ },
+ "@vitest/browser": {
+ "optional": true
+ },
+ "@vitest/ui": {
+ "optional": true
+ },
+ "happy-dom": {
+ "optional": true
+ },
+ "jsdom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vitest/node_modules/tinypool": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.3.0.tgz",
+ "integrity": "sha512-NX5KeqHOBZU6Bc0xj9Vr5Szbb1j8tUHIeD18s41aDJaPeC5QTdEhK0SpdpUrZlj2nv5cctNcSjaKNanXlfcVEQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/vitest/node_modules/vite-node": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.27.1.tgz",
+ "integrity": "sha512-d6+ue/3NzsfndWaPbYh/bFkHbmAWfDXI4B874zRx+WREnG6CUHUbBC8lKaRYZjeR6gCPN5m1aVNNRXBYICA9XA==",
+ "dev": true,
+ "dependencies": {
+ "cac": "^6.7.14",
+ "debug": "^4.3.4",
+ "mlly": "^1.1.0",
+ "pathe": "^0.2.0",
+ "picocolors": "^1.0.0",
+ "source-map": "^0.6.1",
+ "source-map-support": "^0.5.21",
+ "vite": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "vite-node": "vite-node.mjs"
+ },
+ "engines": {
+ "node": ">=v14.16.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
"node_modules/vue": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.45.tgz",
@@ -5445,6 +5781,22 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "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/word-wrap": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
@@ -6160,6 +6512,21 @@
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
"integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A=="
},
+ "@types/chai": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz",
+ "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==",
+ "dev": true
+ },
+ "@types/chai-subset": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.3.tgz",
+ "integrity": "sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==",
+ "dev": true,
+ "requires": {
+ "@types/chai": "*"
+ }
+ },
"@types/flexsearch": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/@types/flexsearch/-/flexsearch-0.7.3.tgz",
@@ -6312,6 +6679,12 @@
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz",
"integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg=="
},
+ "@vue/test-utils": {
+ "version": "2.2.7",
+ "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.2.7.tgz",
+ "integrity": "sha512-BMuoruUFTEqhLoOgsMcgNVMiByYbfHCKGr2C4CPdGtz/affUtDVX5zr1RnPuq0tYSiaqq+Enw5voUpG6JY8Q7g==",
+ "requires": {}
+ },
"abab": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
@@ -6411,6 +6784,12 @@
"es-shim-unscopables": "^1.0.0"
}
},
+ "assertion-error": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+ "dev": true
+ },
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -6490,6 +6869,12 @@
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
},
+ "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
+ },
"call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
@@ -6533,6 +6918,21 @@
"upper-case-first": "^2.0.2"
}
},
+ "chai": {
+ "version": "4.3.7",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz",
+ "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==",
+ "dev": true,
+ "requires": {
+ "assertion-error": "^1.1.0",
+ "check-error": "^1.0.2",
+ "deep-eql": "^4.1.2",
+ "get-func-name": "^2.0.0",
+ "loupe": "^2.3.1",
+ "pathval": "^1.1.1",
+ "type-detect": "^4.0.5"
+ }
+ },
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -6562,6 +6962,12 @@
"tslib": "^2.0.3"
}
},
+ "check-error": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
+ "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==",
+ "dev": true
+ },
"chevrotain": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-4.1.1.tgz",
@@ -6682,6 +7088,12 @@
"which": "^2.0.1"
}
},
+ "css.escape": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
+ "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==",
+ "dev": true
+ },
"cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@@ -6735,6 +7147,15 @@
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
"integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA=="
},
+ "deep-eql": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz",
+ "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==",
+ "dev": true,
+ "requires": {
+ "type-detect": "^4.0.0"
+ }
+ },
"deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -7679,6 +8100,12 @@
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
},
+ "get-func-name": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
+ "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==",
+ "dev": true
+ },
"get-intrinsic": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
@@ -7792,6 +8219,20 @@
}
}
},
+ "happy-dom": {
+ "version": "8.1.4",
+ "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-8.1.4.tgz",
+ "integrity": "sha512-mUCzXHhSO6fOQlZwKW6z2f/+rYavKNxNrgY4nJ4dp+r8gTGbTENgMZGfM6eJD0DJPRFF8DFyngXdBF93wF96UA==",
+ "dev": true,
+ "requires": {
+ "css.escape": "^1.5.1",
+ "he": "^1.2.0",
+ "node-fetch": "^2.x.x",
+ "webidl-conversions": "^7.0.0",
+ "whatwg-encoding": "^2.0.0",
+ "whatwg-mimetype": "^3.0.0"
+ }
+ },
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@@ -7836,6 +8277,12 @@
"has-symbols": "^1.0.2"
}
},
+ "he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true
+ },
"header-case": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz",
@@ -8251,6 +8698,12 @@
"uc.micro": "^1.0.1"
}
},
+ "local-pkg": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.2.tgz",
+ "integrity": "sha512-mlERgSPrbxU3BP4qBqAvvwlgW4MTg78iwJdGGnv7kibKjWcJksrG3t6LB5lXI93wXRDvG4NpUgJFmTG4T6rdrg==",
+ "dev": true
+ },
"locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@@ -8269,6 +8722,15 @@
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
},
+ "loupe": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz",
+ "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==",
+ "dev": true,
+ "requires": {
+ "get-func-name": "^2.0.0"
+ }
+ },
"lower-case": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
@@ -8381,14 +8843,14 @@
"integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g=="
},
"mlly": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.0.0.tgz",
- "integrity": "sha512-QL108Hwt+u9bXdWgOI0dhzZfACovn5Aen4Xvc8Jasd9ouRH4NjnrXEiyP3nVvJo91zPlYjVRckta0Nt2zfoR6g==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.1.0.tgz",
+ "integrity": "sha512-cwzBrBfwGC1gYJyfcy8TcZU1f+dbH/T+TuOhtYP2wLv/Fb51/uV7HJQfBPtEupZ2ORLRU1EKFS/QfS3eo9+kBQ==",
"requires": {
"acorn": "^8.8.1",
"pathe": "^1.0.0",
- "pkg-types": "^1.0.0",
- "ufo": "^1.0.0"
+ "pkg-types": "^1.0.1",
+ "ufo": "^1.0.1"
},
"dependencies": {
"pathe": {
@@ -8432,6 +8894,39 @@
"tslib": "^2.0.3"
}
},
+ "node-fetch": {
+ "version": "2.6.8",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.8.tgz",
+ "integrity": "sha512-RZ6dBYuj8dRSfxpUSu+NsdF1dpPpluJxwOp+6IoDp/sH2QNDSvurYsAa+F1WxY2RjA1iP93xhcsUoYbF2XBqVg==",
+ "dev": true,
+ "requires": {
+ "whatwg-url": "^5.0.0"
+ },
+ "dependencies": {
+ "tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "dev": true
+ },
+ "webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+ "dev": true
+ },
+ "whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dev": true,
+ "requires": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ }
+ }
+ },
"node-releases": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz",
@@ -8626,6 +9121,12 @@
"resolved": "https://registry.npmjs.org/pathe/-/pathe-0.2.0.tgz",
"integrity": "sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw=="
},
+ "pathval": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
+ "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
+ "dev": true
+ },
"picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -8915,6 +9416,12 @@
"object-inspect": "^1.9.0"
}
},
+ "siginfo": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+ "dev": true
+ },
"sirv": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.2.tgz",
@@ -8973,6 +9480,12 @@
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
},
+ "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
+ },
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
@@ -9031,6 +9544,15 @@
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="
},
+ "strip-literal": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.0.0.tgz",
+ "integrity": "sha512-5o4LsH1lzBzO9UFH63AJ2ad2/S2AVx6NtjOcaz+VTT2h1RiRvbipW72z8M/lxEhcPHDBQwpDrnTF7sXy/7OwCQ==",
+ "dev": true,
+ "requires": {
+ "acorn": "^8.8.1"
+ }
+ },
"style-mod": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.0.0.tgz",
@@ -9059,11 +9581,23 @@
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="
},
+ "tinybench": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.3.1.tgz",
+ "integrity": "sha512-hGYWYBMPr7p4g5IarQE7XhlyWveh1EKhy4wUBS1LrHXCKYgvz+4/jCqgmJqZxxldesn05vccrtME2RLLZNW7iA==",
+ "dev": true
+ },
"tinypool": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.1.3.tgz",
"integrity": "sha512-2IfcQh7CP46XGWGGbdyO4pjcKqsmVqFAPcXfPxcPXmOWt9cYkTP9HcDmGgsfijYoAEc4z9qcpM/BaBz46Y9/CQ=="
},
+ "tinyspy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-1.0.2.tgz",
+ "integrity": "sha512-bSGlgwLBYf7PnUsQ6WOc6SJ3pGOcd+d8AA6EUnLDDM0kWEstC1JIlSZA3UNliDXhd9ABoS7hiRBDCu+XP/sf1Q==",
+ "dev": true
+ },
"to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
@@ -9150,6 +9684,12 @@
"prelude-ls": "^1.2.1"
}
},
+ "type-detect": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+ "dev": true
+ },
"type-fest": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
@@ -9292,6 +9832,56 @@
"toml-eslint-parser": "^0.4.0"
}
},
+ "vitest": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.27.1.tgz",
+ "integrity": "sha512-1sIpQ1DVFTEn7c1ici1XHcVfdU4nKiBmPtPAtGKJJJLuJjojTv/OHGgcf69P57alM4ty8V4NMv+7Yoi5Cxqx9g==",
+ "dev": true,
+ "requires": {
+ "@types/chai": "^4.3.4",
+ "@types/chai-subset": "^1.3.3",
+ "@types/node": "*",
+ "acorn": "^8.8.1",
+ "acorn-walk": "^8.2.0",
+ "cac": "^6.7.14",
+ "chai": "^4.3.7",
+ "debug": "^4.3.4",
+ "local-pkg": "^0.4.2",
+ "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",
+ "vite-node": "0.27.1",
+ "why-is-node-running": "^2.2.2"
+ },
+ "dependencies": {
+ "tinypool": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.3.0.tgz",
+ "integrity": "sha512-NX5KeqHOBZU6Bc0xj9Vr5Szbb1j8tUHIeD18s41aDJaPeC5QTdEhK0SpdpUrZlj2nv5cctNcSjaKNanXlfcVEQ==",
+ "dev": true
+ },
+ "vite-node": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.27.1.tgz",
+ "integrity": "sha512-d6+ue/3NzsfndWaPbYh/bFkHbmAWfDXI4B874zRx+WREnG6CUHUbBC8lKaRYZjeR6gCPN5m1aVNNRXBYICA9XA==",
+ "dev": true,
+ "requires": {
+ "cac": "^6.7.14",
+ "debug": "^4.3.4",
+ "mlly": "^1.1.0",
+ "pathe": "^0.2.0",
+ "picocolors": "^1.0.0",
+ "source-map": "^0.6.1",
+ "source-map-support": "^0.5.21",
+ "vite": "^3.0.0 || ^4.0.0"
+ }
+ }
+ }
+ },
"vue": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.45.tgz",
@@ -9444,6 +10034,16 @@
"is-typed-array": "^1.1.10"
}
},
+ "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,
+ "requires": {
+ "siginfo": "^2.0.0",
+ "stackback": "0.0.2"
+ }
+ },
"word-wrap": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
diff --git a/package.json b/package.json
index 27fb2de0f5..c9aef901fc 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
"author": "naltatis & andig",
"scripts": {
"build": "vite build",
- "test": "",
+ "test": "TZ=Europe/Berlin vitest",
"lint": "prettier assets './**/*.{yml,yaml,toml}' --write && eslint assets/**/*.js assets/**/*.vue --fix",
"dev": "vite",
"histoire": "histoire dev"
@@ -17,6 +17,7 @@
"@popperjs/core": "^2.11.5",
"@vitejs/plugin-vue": "^3.0.1",
"@vue/compiler-sfc": "^3.2.30",
+ "@vue/test-utils": "^2.2.7",
"axios": "^1.2.1",
"bootstrap": "^5.2.2",
"canvas-confetti": "^1.4.0",
@@ -48,5 +49,9 @@
"node": ">=18.0.0"
},
"license": "MIT",
- "repository": "github:evcc-io/evcc"
+ "repository": "github:evcc-io/evcc",
+ "devDependencies": {
+ "happy-dom": "^8.1.4",
+ "vitest": "^0.27.1"
+ }
}
diff --git a/vitest.config.js b/vitest.config.js
new file mode 100644
index 0000000000..29af457cbe
--- /dev/null
+++ b/vitest.config.js
@@ -0,0 +1,12 @@
+import { mergeConfig } from "vite";
+import { defineConfig } from "vitest/config";
+import viteConfig from "./vite.config";
+
+export default mergeConfig(
+ viteConfig,
+ defineConfig({
+ test: {
+ environment: "happy-dom",
+ },
+ })
+);