diff --git a/assets/js/components/Loadpoints/SettingsModal.vue b/assets/js/components/Loadpoints/SettingsModal.vue index 74bd22c62..ab560ef51 100644 --- a/assets/js/components/Loadpoints/SettingsModal.vue +++ b/assets/js/components/Loadpoints/SettingsModal.vue @@ -163,7 +163,7 @@ import SmartCostLimit from "../Tariff/SmartCostLimit.vue"; import smartCostAvailable from "@/utils/smartCostAvailable"; import SettingsBatteryBoost from "./SettingsBatteryBoost.vue"; import { defineComponent, type PropType } from "vue"; -import { PHASES } from "@/types/evcc"; +import { CURRENCY, PHASES } from "@/types/evcc"; const V = 230; @@ -200,7 +200,7 @@ export default defineComponent({ smartCostLimit: { type: Number as PropType, default: null }, smartCostType: String, tariffGrid: Number, - currency: String, + currency: String as PropType, multipleLoadpoints: Boolean, }, emits: [ diff --git a/assets/js/mixins/collector.js b/assets/js/mixins/collector.js deleted file mode 100644 index 1faef2fa9..000000000 --- a/assets/js/mixins/collector.js +++ /dev/null @@ -1,19 +0,0 @@ -export default { - methods: { - // collect all target component properties from current instance - collectProps(component, state) { - let data = {}; - for (var p in component.props) { - // check in optional state - if (state && p in state) { - data[p] = state[p]; - } - // check in current instance - if (p in this) { - data[p] = this[p]; - } - } - return data; - }, - }, -}; diff --git a/assets/js/mixins/collector.ts b/assets/js/mixins/collector.ts new file mode 100644 index 000000000..7989c060b --- /dev/null +++ b/assets/js/mixins/collector.ts @@ -0,0 +1,23 @@ +import { defineComponent } from "vue"; +import type { State } from "@/types/evcc"; + +export default defineComponent({ + methods: { + // collect all target component properties from current instance + collectProps(component: any, state?: State) { + let data: Record = {}; + for (const prop in component.props) { + const p = prop as keyof State; + // check in optional state + if (state && p in state) { + data[p] = state[p]; + } + // check in current instance + if (p in this) { + data[p] = (this as Partial)[p]; + } + } + return data; + }, + }, +}); diff --git a/assets/js/mixins/formatter.test.js b/assets/js/mixins/formatter.test.js deleted file mode 100644 index 4c82163df..000000000 --- a/assets/js/mixins/formatter.test.js +++ /dev/null @@ -1,208 +0,0 @@ -import { mount, config } from "@vue/test-utils"; -import { describe, expect, test, vi } from "vitest"; -import formatter, { POWER_UNIT } from "./formatter"; -import * as units from "../units"; - -const is12hSpy = vi.spyOn(units, "is12hFormat").mockReturnValue(false); - -config.global.mocks["$i18n"] = { locale: "de-DE" }; -config.global.mocks["$t"] = (a) => a; - -const fmt = mount({ - render() {}, - mixins: [formatter], -}).componentVM; - -describe("fmtW", () => { - test("should format with units", () => { - expect(fmt.fmtW(0, POWER_UNIT.AUTO)).eq("0,0 kW"); - expect(fmt.fmtW(1200000, POWER_UNIT.AUTO)).eq("1.200,0 kW"); - expect(fmt.fmtW(0, POWER_UNIT.MW)).eq("0,0 MW"); - expect(fmt.fmtW(1200000, POWER_UNIT.MW)).eq("1,2 MW"); - expect(fmt.fmtW(0, POWER_UNIT.KW)).eq("0,0 kW"); - expect(fmt.fmtW(1200000, POWER_UNIT.KW)).eq("1.200,0 kW"); - expect(fmt.fmtW(0, POWER_UNIT.W)).eq("0 W"); - expect(fmt.fmtW(1200000, POWER_UNIT.W)).eq("1.200.000 W"); - }); - test("should format without units", () => { - expect(fmt.fmtW(0, POWER_UNIT.AUTO, false)).eq("0,0"); - expect(fmt.fmtW(1200000, POWER_UNIT.AUTO, false)).eq("1.200,0"); - expect(fmt.fmtW(0, POWER_UNIT.MW, false)).eq("0,0"); - expect(fmt.fmtW(1200000, POWER_UNIT.MW, false)).eq("1,2"); - expect(fmt.fmtW(0, POWER_UNIT.KW, false)).eq("0,0"); - expect(fmt.fmtW(1200000, POWER_UNIT.KW, false)).eq("1.200,0"); - expect(fmt.fmtW(0, POWER_UNIT.W, false)).eq("0"); - expect(fmt.fmtW(1200000, POWER_UNIT.W, false)).eq("1.200.000"); - }); - test("should format a given number of digits", () => { - expect(fmt.fmtW(12345, POWER_UNIT.AUTO, true, 0)).eq("12 kW"); - expect(fmt.fmtW(12345, POWER_UNIT.AUTO, true, 1)).eq("12,3 kW"); - expect(fmt.fmtW(12345, POWER_UNIT.AUTO, true, 2)).eq("12,35 kW"); - expect(fmt.fmtW(12345, POWER_UNIT.MW, true, 0)).eq("0 MW"); - expect(fmt.fmtW(12345, POWER_UNIT.MW, true, 1)).eq("0,0 MW"); - expect(fmt.fmtW(12345, POWER_UNIT.MW, true, 2)).eq("0,01 MW"); - expect(fmt.fmtW(12345, POWER_UNIT.KW, true, 0)).eq("12 kW"); - expect(fmt.fmtW(12345, POWER_UNIT.KW, true, 1)).eq("12,3 kW"); - expect(fmt.fmtW(12345, POWER_UNIT.KW, true, 2)).eq("12,35 kW"); - expect(fmt.fmtW(12345, POWER_UNIT.W, true, 0)).eq("12.345 W"); - expect(fmt.fmtW(12345, POWER_UNIT.W, true, 1)).eq("12.345,0 W"); - expect(fmt.fmtW(12345, POWER_UNIT.W, true, 2)).eq("12.345,00 W"); - }); -}); - -describe("fmtWh", () => { - test("should format with units", () => { - expect(fmt.fmtWh(0, POWER_UNIT.AUTO)).eq("0,0 kWh"); - expect(fmt.fmtWh(1200000, POWER_UNIT.AUTO)).eq("1.200,0 kWh"); - expect(fmt.fmtWh(0, POWER_UNIT.MW)).eq("0,0 MWh"); - expect(fmt.fmtWh(1200000, POWER_UNIT.MW)).eq("1,2 MWh"); - expect(fmt.fmtWh(0, POWER_UNIT.KW)).eq("0,0 kWh"); - expect(fmt.fmtWh(1200000, POWER_UNIT.KW)).eq("1.200,0 kWh"); - expect(fmt.fmtWh(0, POWER_UNIT.W)).eq("0 Wh"); - expect(fmt.fmtWh(1200000, POWER_UNIT.W)).eq("1.200.000 Wh"); - }); - test("should format without units", () => { - expect(fmt.fmtWh(0, POWER_UNIT.AUTO, false)).eq("0,0"); - expect(fmt.fmtWh(1200000, POWER_UNIT.AUTO, false)).eq("1.200,0"); - expect(fmt.fmtWh(0, POWER_UNIT.MW, false)).eq("0,0"); - expect(fmt.fmtWh(1200000, POWER_UNIT.MW, false)).eq("1,2"); - expect(fmt.fmtWh(0, POWER_UNIT.KW, false)).eq("0,0"); - expect(fmt.fmtWh(1200000, POWER_UNIT.KW, false)).eq("1.200,0"); - expect(fmt.fmtWh(0, POWER_UNIT.W, false)).eq("0"); - expect(fmt.fmtWh(1200000, POWER_UNIT.W, false)).eq("1.200.000"); - }); - test("should format a given number of digits", () => { - expect(fmt.fmtWh(12345, POWER_UNIT.AUTO, true, 0)).eq("12 kWh"); - expect(fmt.fmtWh(12345, POWER_UNIT.AUTO, true, 1)).eq("12,3 kWh"); - expect(fmt.fmtWh(12345, POWER_UNIT.AUTO, true, 2)).eq("12,35 kWh"); - expect(fmt.fmtWh(12345, POWER_UNIT.MW, true, 0)).eq("0 MWh"); - expect(fmt.fmtWh(12345, POWER_UNIT.MW, true, 1)).eq("0,0 MWh"); - expect(fmt.fmtWh(12345, POWER_UNIT.MW, true, 2)).eq("0,01 MWh"); - expect(fmt.fmtWh(12345, POWER_UNIT.KW, true, 0)).eq("12 kWh"); - expect(fmt.fmtWh(12345, POWER_UNIT.KW, true, 1)).eq("12,3 kWh"); - expect(fmt.fmtWh(12345, POWER_UNIT.KW, true, 2)).eq("12,35 kWh"); - expect(fmt.fmtWh(12345, POWER_UNIT.W, true, 0)).eq("12.345 Wh"); - expect(fmt.fmtWh(12345, POWER_UNIT.W, true, 1)).eq("12.345,0 Wh"); - expect(fmt.fmtWh(12345, POWER_UNIT.W, true, 2)).eq("12.345,00 Wh"); - }); -}); - -describe("fmtPricePerKWh", () => { - test("should format with units", () => { - expect(fmt.fmtPricePerKWh(0.2, "EUR")).eq("20,0 ct/kWh"); - expect(fmt.fmtPricePerKWh(0.2, "EUR", true)).eq("20,0 ct"); - expect(fmt.fmtPricePerKWh(0.234, "USD")).eq("23,4 ¢/kWh"); - expect(fmt.fmtPricePerKWh(1234, "SEK")).eq("1.234,0 SEK/kWh"); - expect(fmt.fmtPricePerKWh(0.2, "EUR", false, false)).eq("20,0"); - expect(fmt.fmtPricePerKWh(0.123, "CHF")).eq("12,3 rp/kWh"); - }); -}); - -describe("pricePerKWhUnit", () => { - test("should return correct unit", () => { - expect(fmt.pricePerKWhUnit("EUR")).eq("ct/kWh"); - expect(fmt.pricePerKWhUnit("EUR", true)).eq("ct"); - expect(fmt.pricePerKWhUnit("USD")).eq("¢/kWh"); - expect(fmt.pricePerKWhUnit("SEK")).eq("SEK/kWh"); - expect(fmt.pricePerKWhUnit("SEK", true)).eq("SEK"); - expect(fmt.pricePerKWhUnit("CHF")).eq("rp/kWh"); - }); -}); - -describe("fmtDuration", () => { - test("should format zero duration", () => { - expect(fmt.fmtDuration(0)).eq("—"); - expect(fmt.fmtDuration(-100)).eq("—"); - }); - test("should format seconds", () => { - expect(fmt.fmtDuration(1)).eq("1\u202Fs"); - expect(fmt.fmtDuration(59)).eq("59\u202Fs"); - expect(fmt.fmtDuration(59, false)).eq("59"); - expect(fmt.fmtDuration(59, true, "m")).eq("0:59\u202Fm"); - expect(fmt.fmtDuration(59, true, "h")).eq("0:00\u202Fh"); - }); - test("should format minutes", () => { - expect(fmt.fmtDuration(60)).eq("1:00\u202Fm"); - expect(fmt.fmtDuration(150)).eq("2:30\u202Fm"); - expect(fmt.fmtDuration(150, false)).eq("2:30"); - expect(fmt.fmtDuration(150, true, "h")).eq("0:02\u202Fh"); - }); - test("should format hours", () => { - expect(fmt.fmtDuration(60 * 60)).eq("1:00\u202Fh"); - expect(fmt.fmtDuration(60 * 60 * 2.5)).eq("2:30\u202Fh"); - expect(fmt.fmtDuration(60 * 60 * 2.5, false)).eq("2:30"); - }); - test("should format internationalized", () => { - config.global.mocks["$i18n"].locale = "ar-EG"; - expect(fmt.fmtDuration(30, false)).eq("٣٠"); - expect(fmt.fmtDuration(90, false)).eq("١:٣٠"); - expect(fmt.fmtDuration(60 * 100, false)).eq("١:٤٠"); - config.global.mocks["$i18n"].locale = "de-DE"; - }); -}); - -describe("getWeekdaysList", () => { - test("should return the correct weekday-order", () => { - expect(fmt.getWeekdaysList("long")).toEqual([ - { name: "Montag", value: 1 }, - { name: "Dienstag", value: 2 }, - { name: "Mittwoch", value: 3 }, - { name: "Donnerstag", value: 4 }, - { name: "Freitag", value: 5 }, - { name: "Samstag", value: 6 }, - { name: "Sonntag", value: 0 }, - ]); - }); -}); - -describe("getShortenedWeekdaysLabel", () => { - test("should format single days", () => { - expect(fmt.getShortenedWeekdaysLabel([0])).eq("So"); - expect(fmt.getShortenedWeekdaysLabel([0, 2, 4, 6])).eq("Di, Do, Sa, So"); - expect(fmt.getShortenedWeekdaysLabel([6])).eq("Sa"); - expect(fmt.getShortenedWeekdaysLabel([3, 6])).eq("Mi, Sa"); - }); - test("should format ranges", () => { - expect(fmt.getShortenedWeekdaysLabel([1, 2])).eq("Mo, Di"); - expect(fmt.getShortenedWeekdaysLabel([0, 1, 2, 3, 4, 5, 6])).eq("Mo – So"); - expect(fmt.getShortenedWeekdaysLabel([0, 1, 3, 4, 5])).eq("Mo, Mi – Fr, So"); - }); - test("should format single days and ranges", () => { - expect(fmt.getShortenedWeekdaysLabel([0, 1, 3, 5, 6])).eq("Mo, Mi, Fr – So"); - expect(fmt.getShortenedWeekdaysLabel([0, 2, 3, 5, 6])).eq("Di, Mi, Fr – So"); - }); -}); - -describe("fmtPercentage", () => { - test("should format percentage", () => { - expect(fmt.fmtPercentage(0)).eq("0 %"); - expect(fmt.fmtPercentage(100)).eq("100 %"); - expect(fmt.fmtPercentage(100, 1)).eq("100,0 %"); - }); - test("should force signed", () => { - expect(fmt.fmtPercentage(100, 0, true)).eq("+100 %"); - expect(fmt.fmtPercentage(100, 0, false)).eq("100 %"); - expect(fmt.fmtPercentage(-100, 0, true)).eq("-100 %"); - expect(fmt.fmtPercentage(-100, 0, false)).eq("-100 %"); - }); -}); - -const testDate = new Date(2023, 0, 15, 15, 30); // Jan 15, 2023, 15:30 (3:30 PM) - -describe("12h/24h time format", () => { - test("12h format", () => { - is12hSpy.mockReturnValue(true); - expect(fmt.fmtHourMinute(testDate)).toBe("3:30 PM"); - expect(fmt.fmtFullDateTime(testDate)).toBe("So., 15. Jan., 3:30 PM"); - expect(fmt.fmtWeekdayTime(testDate)).toBe("So. 3:30 PM"); - expect(fmt.fmtAbsoluteDate(testDate)).toBe("So 3:30 PM"); - }); - - test("24h format", () => { - is12hSpy.mockReturnValue(false); - expect(fmt.fmtHourMinute(testDate)).toBe("15:30"); - expect(fmt.fmtFullDateTime(testDate)).toBe("So., 15. Jan., 15:30"); - expect(fmt.fmtWeekdayTime(testDate)).toBe("So., 15:30"); - expect(fmt.fmtAbsoluteDate(testDate)).toBe("So 15:30"); - }); -}); diff --git a/assets/js/mixins/formatter.test.ts b/assets/js/mixins/formatter.test.ts new file mode 100644 index 000000000..874c7dbfc --- /dev/null +++ b/assets/js/mixins/formatter.test.ts @@ -0,0 +1,212 @@ +import { mount, config } from "@vue/test-utils"; +import { describe, expect, test, vi } from "vitest"; +import formatter, { POWER_UNIT } from "./formatter"; +import * as units from "../units"; +import { defineComponent } from "vue"; +import { CURRENCY } from "@/types/evcc"; + +const is12hSpy = vi.spyOn(units, "is12hFormat").mockReturnValue(false); + +config.global.mocks["$i18n"] = { locale: "de-DE" }; +config.global.mocks["$t"] = (a: string) => a; + +const fmt = mount( + defineComponent({ + render() {}, + mixins: [formatter], + }) +).vm; + +describe("fmtW", () => { + test("should format with units", () => { + expect(fmt.fmtW(0, POWER_UNIT.AUTO)).eq("0,0 kW"); + expect(fmt.fmtW(1200000, POWER_UNIT.AUTO)).eq("1.200,0 kW"); + expect(fmt.fmtW(0, POWER_UNIT.MW)).eq("0,0 MW"); + expect(fmt.fmtW(1200000, POWER_UNIT.MW)).eq("1,2 MW"); + expect(fmt.fmtW(0, POWER_UNIT.KW)).eq("0,0 kW"); + expect(fmt.fmtW(1200000, POWER_UNIT.KW)).eq("1.200,0 kW"); + expect(fmt.fmtW(0, POWER_UNIT.W)).eq("0 W"); + expect(fmt.fmtW(1200000, POWER_UNIT.W)).eq("1.200.000 W"); + }); + test("should format without units", () => { + expect(fmt.fmtW(0, POWER_UNIT.AUTO, false)).eq("0,0"); + expect(fmt.fmtW(1200000, POWER_UNIT.AUTO, false)).eq("1.200,0"); + expect(fmt.fmtW(0, POWER_UNIT.MW, false)).eq("0,0"); + expect(fmt.fmtW(1200000, POWER_UNIT.MW, false)).eq("1,2"); + expect(fmt.fmtW(0, POWER_UNIT.KW, false)).eq("0,0"); + expect(fmt.fmtW(1200000, POWER_UNIT.KW, false)).eq("1.200,0"); + expect(fmt.fmtW(0, POWER_UNIT.W, false)).eq("0"); + expect(fmt.fmtW(1200000, POWER_UNIT.W, false)).eq("1.200.000"); + }); + test("should format a given number of digits", () => { + expect(fmt.fmtW(12345, POWER_UNIT.AUTO, true, 0)).eq("12 kW"); + expect(fmt.fmtW(12345, POWER_UNIT.AUTO, true, 1)).eq("12,3 kW"); + expect(fmt.fmtW(12345, POWER_UNIT.AUTO, true, 2)).eq("12,35 kW"); + expect(fmt.fmtW(12345, POWER_UNIT.MW, true, 0)).eq("0 MW"); + expect(fmt.fmtW(12345, POWER_UNIT.MW, true, 1)).eq("0,0 MW"); + expect(fmt.fmtW(12345, POWER_UNIT.MW, true, 2)).eq("0,01 MW"); + expect(fmt.fmtW(12345, POWER_UNIT.KW, true, 0)).eq("12 kW"); + expect(fmt.fmtW(12345, POWER_UNIT.KW, true, 1)).eq("12,3 kW"); + expect(fmt.fmtW(12345, POWER_UNIT.KW, true, 2)).eq("12,35 kW"); + expect(fmt.fmtW(12345, POWER_UNIT.W, true, 0)).eq("12.345 W"); + expect(fmt.fmtW(12345, POWER_UNIT.W, true, 1)).eq("12.345,0 W"); + expect(fmt.fmtW(12345, POWER_UNIT.W, true, 2)).eq("12.345,00 W"); + }); +}); + +describe("fmtWh", () => { + test("should format with units", () => { + expect(fmt.fmtWh(0, POWER_UNIT.AUTO)).eq("0,0 kWh"); + expect(fmt.fmtWh(1200000, POWER_UNIT.AUTO)).eq("1.200,0 kWh"); + expect(fmt.fmtWh(0, POWER_UNIT.MW)).eq("0,0 MWh"); + expect(fmt.fmtWh(1200000, POWER_UNIT.MW)).eq("1,2 MWh"); + expect(fmt.fmtWh(0, POWER_UNIT.KW)).eq("0,0 kWh"); + expect(fmt.fmtWh(1200000, POWER_UNIT.KW)).eq("1.200,0 kWh"); + expect(fmt.fmtWh(0, POWER_UNIT.W)).eq("0 Wh"); + expect(fmt.fmtWh(1200000, POWER_UNIT.W)).eq("1.200.000 Wh"); + }); + test("should format without units", () => { + expect(fmt.fmtWh(0, POWER_UNIT.AUTO, false)).eq("0,0"); + expect(fmt.fmtWh(1200000, POWER_UNIT.AUTO, false)).eq("1.200,0"); + expect(fmt.fmtWh(0, POWER_UNIT.MW, false)).eq("0,0"); + expect(fmt.fmtWh(1200000, POWER_UNIT.MW, false)).eq("1,2"); + expect(fmt.fmtWh(0, POWER_UNIT.KW, false)).eq("0,0"); + expect(fmt.fmtWh(1200000, POWER_UNIT.KW, false)).eq("1.200,0"); + expect(fmt.fmtWh(0, POWER_UNIT.W, false)).eq("0"); + expect(fmt.fmtWh(1200000, POWER_UNIT.W, false)).eq("1.200.000"); + }); + test("should format a given number of digits", () => { + expect(fmt.fmtWh(12345, POWER_UNIT.AUTO, true, 0)).eq("12 kWh"); + expect(fmt.fmtWh(12345, POWER_UNIT.AUTO, true, 1)).eq("12,3 kWh"); + expect(fmt.fmtWh(12345, POWER_UNIT.AUTO, true, 2)).eq("12,35 kWh"); + expect(fmt.fmtWh(12345, POWER_UNIT.MW, true, 0)).eq("0 MWh"); + expect(fmt.fmtWh(12345, POWER_UNIT.MW, true, 1)).eq("0,0 MWh"); + expect(fmt.fmtWh(12345, POWER_UNIT.MW, true, 2)).eq("0,01 MWh"); + expect(fmt.fmtWh(12345, POWER_UNIT.KW, true, 0)).eq("12 kWh"); + expect(fmt.fmtWh(12345, POWER_UNIT.KW, true, 1)).eq("12,3 kWh"); + expect(fmt.fmtWh(12345, POWER_UNIT.KW, true, 2)).eq("12,35 kWh"); + expect(fmt.fmtWh(12345, POWER_UNIT.W, true, 0)).eq("12.345 Wh"); + expect(fmt.fmtWh(12345, POWER_UNIT.W, true, 1)).eq("12.345,0 Wh"); + expect(fmt.fmtWh(12345, POWER_UNIT.W, true, 2)).eq("12.345,00 Wh"); + }); +}); + +describe("fmtPricePerKWh", () => { + test("should format with units", () => { + expect(fmt.fmtPricePerKWh(0.2, CURRENCY.EUR)).eq("20,0 ct/kWh"); + expect(fmt.fmtPricePerKWh(0.2, CURRENCY.EUR, true)).eq("20,0 ct"); + expect(fmt.fmtPricePerKWh(0.234, CURRENCY.USD)).eq("23,4 ¢/kWh"); + expect(fmt.fmtPricePerKWh(1234, CURRENCY.SEK)).eq("123.400,0 öre/kWh"); + expect(fmt.fmtPricePerKWh(0.2, CURRENCY.EUR, false, false)).eq("20,0"); + expect(fmt.fmtPricePerKWh(0.123, CURRENCY.CHF)).eq("12,3 rp/kWh"); + }); +}); + +describe("pricePerKWhUnit", () => { + test("should return correct unit", () => { + expect(fmt.pricePerKWhUnit(CURRENCY.EUR)).eq("ct/kWh"); + expect(fmt.pricePerKWhUnit(CURRENCY.EUR, true)).eq("ct"); + expect(fmt.pricePerKWhUnit(CURRENCY.USD)).eq("¢/kWh"); + expect(fmt.pricePerKWhUnit(CURRENCY.SEK)).eq("öre/kWh"); + expect(fmt.pricePerKWhUnit(CURRENCY.SEK, true)).eq("öre"); + expect(fmt.pricePerKWhUnit(CURRENCY.CHF)).eq("rp/kWh"); + }); +}); + +describe("fmtDuration", () => { + test("should format zero duration", () => { + expect(fmt.fmtDuration(0)).eq("—"); + expect(fmt.fmtDuration(-100)).eq("—"); + }); + test("should format seconds", () => { + expect(fmt.fmtDuration(1)).eq("1\u202Fs"); + expect(fmt.fmtDuration(59)).eq("59\u202Fs"); + expect(fmt.fmtDuration(59, false)).eq("59"); + expect(fmt.fmtDuration(59, true, "m")).eq("0:59\u202Fm"); + expect(fmt.fmtDuration(59, true, "h")).eq("0:00\u202Fh"); + }); + test("should format minutes", () => { + expect(fmt.fmtDuration(60)).eq("1:00\u202Fm"); + expect(fmt.fmtDuration(150)).eq("2:30\u202Fm"); + expect(fmt.fmtDuration(150, false)).eq("2:30"); + expect(fmt.fmtDuration(150, true, "h")).eq("0:02\u202Fh"); + }); + test("should format hours", () => { + expect(fmt.fmtDuration(60 * 60)).eq("1:00\u202Fh"); + expect(fmt.fmtDuration(60 * 60 * 2.5)).eq("2:30\u202Fh"); + expect(fmt.fmtDuration(60 * 60 * 2.5, false)).eq("2:30"); + }); + test("should format internationalized", () => { + config.global.mocks["$i18n"].locale = "ar-EG"; + expect(fmt.fmtDuration(30, false)).eq("٣٠"); + expect(fmt.fmtDuration(90, false)).eq("١:٣٠"); + expect(fmt.fmtDuration(60 * 100, false)).eq("١:٤٠"); + config.global.mocks["$i18n"].locale = "de-DE"; + }); +}); + +describe("getWeekdaysList", () => { + test("should return the correct weekday-order", () => { + expect(fmt.getWeekdaysList("long")).toEqual([ + { name: "Montag", value: 1 }, + { name: "Dienstag", value: 2 }, + { name: "Mittwoch", value: 3 }, + { name: "Donnerstag", value: 4 }, + { name: "Freitag", value: 5 }, + { name: "Samstag", value: 6 }, + { name: "Sonntag", value: 0 }, + ]); + }); +}); + +describe("getShortenedWeekdaysLabel", () => { + test("should format single days", () => { + expect(fmt.getShortenedWeekdaysLabel([0])).eq("So"); + expect(fmt.getShortenedWeekdaysLabel([0, 2, 4, 6])).eq("Di, Do, Sa, So"); + expect(fmt.getShortenedWeekdaysLabel([6])).eq("Sa"); + expect(fmt.getShortenedWeekdaysLabel([3, 6])).eq("Mi, Sa"); + }); + test("should format ranges", () => { + expect(fmt.getShortenedWeekdaysLabel([1, 2])).eq("Mo, Di"); + expect(fmt.getShortenedWeekdaysLabel([0, 1, 2, 3, 4, 5, 6])).eq("Mo – So"); + expect(fmt.getShortenedWeekdaysLabel([0, 1, 3, 4, 5])).eq("Mo, Mi – Fr, So"); + }); + test("should format single days and ranges", () => { + expect(fmt.getShortenedWeekdaysLabel([0, 1, 3, 5, 6])).eq("Mo, Mi, Fr – So"); + expect(fmt.getShortenedWeekdaysLabel([0, 2, 3, 5, 6])).eq("Di, Mi, Fr – So"); + }); +}); + +describe("fmtPercentage", () => { + test("should format percentage", () => { + expect(fmt.fmtPercentage(0)).eq("0 %"); + expect(fmt.fmtPercentage(100)).eq("100 %"); + expect(fmt.fmtPercentage(100, 1)).eq("100,0 %"); + }); + test("should force signed", () => { + expect(fmt.fmtPercentage(100, 0, true)).eq("+100 %"); + expect(fmt.fmtPercentage(100, 0, false)).eq("100 %"); + expect(fmt.fmtPercentage(-100, 0, true)).eq("-100 %"); + expect(fmt.fmtPercentage(-100, 0, false)).eq("-100 %"); + }); +}); + +const testDate = new Date(2023, 0, 15, 15, 30); // Jan 15, 2023, 15:30 (3:30 PM) + +describe("12h/24h time format", () => { + test("12h format", () => { + is12hSpy.mockReturnValue(true); + expect(fmt.fmtHourMinute(testDate)).toBe("3:30 PM"); + expect(fmt.fmtFullDateTime(testDate, false)).toBe("So., 15. Jan., 3:30 PM"); + expect(fmt.fmtWeekdayTime(testDate)).toBe("So. 3:30 PM"); + expect(fmt.fmtAbsoluteDate(testDate)).toBe("So 3:30 PM"); + }); + + test("24h format", () => { + is12hSpy.mockReturnValue(false); + expect(fmt.fmtHourMinute(testDate)).toBe("15:30"); + expect(fmt.fmtFullDateTime(testDate, false)).toBe("So., 15. Jan., 15:30"); + expect(fmt.fmtWeekdayTime(testDate)).toBe("So., 15:30"); + expect(fmt.fmtAbsoluteDate(testDate)).toBe("So 15:30"); + }); +}); diff --git a/assets/js/mixins/formatter.ts b/assets/js/mixins/formatter.ts index d54cd75a2..595217087 100644 --- a/assets/js/mixins/formatter.ts +++ b/assets/js/mixins/formatter.ts @@ -2,8 +2,25 @@ import { defineComponent } from "vue"; import { is12hFormat } from "@/units"; import { CURRENCY } from "../types/evcc"; +const CURRENCY_SYMBOLS: Record = { + AUD: "$", + BGN: "лв", + BRL: "R$", + CAD: "$", + CHF: "Fr.", + CNY: "¥", + EUR: "€", + GBP: "£", + ILS: "₪", + NZD: "$", + PLN: "zł", + USD: "$", + DKK: "kr", + SEK: "kr", +}; + // list of currencies where energy price should be displayed in subunits (factor 100) -const ENERGY_PRICE_IN_SUBUNIT = { +const ENERGY_PRICE_IN_SUBUNIT: Record = { AUD: "c", // Australian cent BGN: "st", // Bulgarian stotinka BRL: "¢", // Brazilian centavo @@ -15,8 +32,9 @@ const ENERGY_PRICE_IN_SUBUNIT = { ILS: "ag", // Israeli agora NZD: "c", // New Zealand cent PLN: "gr", // Polish grosz - USD: "¢", // US cent, + USD: "¢", // US cent DKK: "øre", // Danish øre + SEK: "öre", // Swedish öre }; export enum POWER_UNIT { @@ -283,8 +301,7 @@ export default defineComponent({ return withSymbol ? result : result.replace(currency, "").trim(); }, fmtCurrencySymbol(currency = CURRENCY.EUR) { - const symbols = { EUR: "€", USD: "$", DKK: "dkr." }; - return symbols[currency] || currency; + return CURRENCY_SYMBOLS[currency] || currency; }, fmtPricePerKWh(amout = 0, currency = CURRENCY.EUR, short = false, withUnit = true) { let value = amout; diff --git a/assets/js/mixins/icon.js b/assets/js/mixins/icon.js deleted file mode 100644 index 5bf6d1212..000000000 --- a/assets/js/mixins/icon.js +++ /dev/null @@ -1,23 +0,0 @@ -export default { - props: { - size: { - type: String, - validator(value) { - return ["xs", "s", "m", "l", "xl"].includes(value); - }, - }, - }, - computed: { - svgStyle() { - const sizes = { - xs: "16px", - s: "24px", - m: "32px", - l: "48px", - xl: "64px", - }; - const size = sizes[this.size] || sizes.s; - return { display: "block", width: size, height: size }; - }, - }, -}; diff --git a/assets/js/mixins/icon.ts b/assets/js/mixins/icon.ts new file mode 100644 index 000000000..0a4c45b6f --- /dev/null +++ b/assets/js/mixins/icon.ts @@ -0,0 +1,27 @@ +import { ICON_SIZE } from "@/types/evcc"; +import { defineComponent, type PropType } from "vue"; + +export default defineComponent({ + props: { + size: { + type: String as PropType, + validator(value: string) { + return Object.keys(ICON_SIZE).includes(value); + }, + default: ICON_SIZE.S, + }, + }, + computed: { + svgStyle() { + const sizes: Record = { + xs: "16px", + s: "24px", + m: "32px", + l: "48px", + xl: "64px", + }; + const size = sizes[this.size]; + return { display: "block", width: size, height: size }; + }, + }, +}); diff --git a/assets/js/types/evcc.ts b/assets/js/types/evcc.ts index ee9ac3fc9..f83dd6714 100644 --- a/assets/js/types/evcc.ts +++ b/assets/js/types/evcc.ts @@ -49,9 +49,20 @@ export interface LoadpointCompact { } export enum CURRENCY { + AUD = "AUD", + BGN = "BGN", + BRL = "BRL", + CAD = "CAD", + CHF = "CHF", + CNY = "CNY", EUR = "EUR", + GBP = "GBP", + ILS = "ILS", + NZD = "NZD", + PLN = "PLN", USD = "USD", DKK = "DKK", + SEK = "SEK", } export enum ICON_SIZE { diff --git a/tests/config-tariffs.spec.js b/tests/config-tariffs.spec.js index c8c7d8e6f..0256be1be 100644 --- a/tests/config-tariffs.spec.js +++ b/tests/config-tariffs.spec.js @@ -94,9 +94,9 @@ test.describe("tariffs", async () => { "Currency", "SEK", "Grid price", - "0.3 SEK", + "30.0 öre", "Feed-in price", - "-0.1 SEK", + "-10.0 öre", "Grid CO₂", "300 g", ].join("") diff --git a/tests/statistics.spec.js b/tests/statistics.spec.js index a5c4400ea..1d0a5b09c 100644 --- a/tests/statistics.spec.js +++ b/tests/statistics.spec.js @@ -55,7 +55,7 @@ test.describe("statistics values", async () => { await expect(page.getByTestId("savings-tile-solar")).toContainText("20 kWh grid"); await expect(page.getByTestId("savings-tile-price")).toContainText("18.0rp/kWh"); - await expect(page.getByTestId("savings-tile-price")).toContainText("6 CHF saved"); + await expect(page.getByTestId("savings-tile-price")).toContainText("6 Fr. saved"); await expect(page.getByTestId("savings-tile-co2")).toContainText("8g/kWh"); await expect(page.getByTestId("savings-tile-co2")).toContainText("19 kg saved"); @@ -75,7 +75,7 @@ test.describe("statistics values", async () => { await expect(page.getByTestId("savings-tile-solar")).toContainText("70 kWh grid"); await expect(page.getByTestId("savings-tile-price")).toContainText("24.0rp/kWh"); - await expect(page.getByTestId("savings-tile-price")).toContainText("6 CHF saved"); + await expect(page.getByTestId("savings-tile-price")).toContainText("6 Fr. saved"); await expect(page.getByTestId("savings-tile-co2")).toContainText("14g/kWh"); await expect(page.getByTestId("savings-tile-co2")).toContainText("37 kg saved");