Files
audi-app/companion-app/src/format.test.ts
T
tobias 0f680780a4 Einzelfahrt: Orte aus den Koordinaten, "Min." statt "min" (2026.9.1.20)
START UND ZIEL STANDEN AUF "UNBEKANNT"

start_address/end_address stehen in HANDFELDER - das Backend fuellt sie nie von
selbst, sie kommen nur aus einer von Hand angelegten oder bearbeiteten Fahrt.
Die Koordinaten dagegen traegt das Screening ein: 12 von 14 Fahrten im
Testbestand hatten Start- und Zielposition, aber genau eine hatte eine Adresse.

Beide Oberflaechen loesen sie jetzt beim Oeffnen der Einzelfahrt auf, ueber
denselben Cache wie die Standortansicht - ein Abruf je Ort, danach nie wieder.
Bewusst NICHT in der Liste: ein Vorratsabruf fuer alle Fahrten waere genau das,
worum Nominatim in seinen Nutzungsbedingungen bittet, es nicht zu tun.

Bis zur Antwort steht "wird ermittelt ...", ohne Koordinaten oder ohne Treffer
"unbekannt". Das Panel prueft vor dem Eintragen, ob noch dieselbe Fahrt offen
ist - wer waehrend des Abrufs weiterblaettert, soll nicht die Adresse der
vorigen Fahrt in der neuen sehen.

Live: Start "Schottenau, 85072 Eichstaett", Ziel "Am Anger 7a, 85111
Adelschlag".

"MIN." STATT "MIN"

Deutsche Abkuerzung, wie die Oberflaeche sie an anderer Stelle laengst
verwendet ("Geparkt seit 2 Tg. 16 Std. 12 Min."). In dauerText() bzw. dauer(),
also ueberall wo eine Dauer erscheint. Zwei Testerwartungen mitgezogen.

Verifiziert: Panel als Modul, tsc --noEmit und vite build sauber, 165/165
Tests gruen, im Browser nachgesehen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 19:38:09 +02:00

104 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it } from "vitest"
import { alsCsv } from "./screens/csv"
import { kalenderDatei } from "./screens/kalender"
import { dauer, de, deOderStrich, eur, isoTag, schnittpreis, summe } from "./format"
describe("Zahlen im deutschen Format", () => {
it("setzt Punkt als Tausender- und Komma als Dezimaltrennzeichen", () => {
expect(de(48250)).toBe("48.250")
expect(de(1234.5, 1)).toBe("1.234,5")
expect(eur(89.1)).toBe("89,10")
})
it("macht aus fehlenden Werten eine Null, nicht NaN", () => {
expect(de(null)).toBe("0")
expect(de(undefined)).toBe("0")
})
it("unterscheidet mit deOderStrich zwischen null und unbekannt", () => {
expect(deOderStrich(0)).toBe("0")
expect(deOderStrich(null)).toBe("")
})
})
describe("dauer", () => {
it("zeigt unter einer Stunde nur Minuten", () => {
expect(dauer(2700)).toBe("45 Min.")
})
it("zeigt darüber Stunden und Minuten zweistellig", () => {
expect(dauer(8040)).toBe("2 h 14 Min.")
})
it("zeigt einen Strich statt „0 min", () => {
expect(dauer(0)).toBe("")
expect(dauer(null)).toBe("")
})
})
describe("schnittpreis", () => {
it("gewichtet nach Menge, nicht nach Anzahl der Tankungen", () => {
// 10 l zu 2,00 € und 90 l zu 1,00 €: der Schnitt muss nahe 1,10 liegen,
// nicht bei 1,50 (dem Mittel der Einzelpreise).
const schnitt = schnittpreis([
{ liters: 10, fuel_total_eur: 20 },
{ liters: 90, fuel_total_eur: 90 },
])
expect(schnitt).toBeCloseTo(1.1, 2)
})
it("liefert 0 statt einer Division durch null", () => {
expect(schnittpreis([])).toBe(0)
expect(schnittpreis([{ liters: 0, fuel_total_eur: 10 }])).toBe(0)
})
})
describe("summe", () => {
it("überspringt fehlende Werte", () => {
expect(summe([{ a: 1 }, { a: null }, { a: 3 }], (e) => e.a ?? 0)).toBe(4)
})
})
describe("isoTag", () => {
it("gibt das lokale Datum aus, nicht das UTC-Datum", () => {
// 23:30 Ortszeit ist in UTC schon der Folgetag — der Tag darf nicht kippen.
const spaet = new Date(2026, 7, 11, 23, 30)
expect(isoTag(spaet)).toBe("2026-08-11")
})
})
describe("CSV-Ausgabe", () => {
it("trennt mit Semikolon und schreibt Dezimalkomma", () => {
const csv = alsCsv(["Strecke", "Ort"], [[42.5, "München"]])
expect(csv).toContain("Strecke;Ort")
expect(csv).toContain("42,5;München")
})
it("beginnt mit einem BOM, damit Excel die Umlaute richtig liest", () => {
expect(alsCsv(["a"], [])[0]).toBe("")
})
it("schützt Felder mit Semikolon oder Anführungszeichen", () => {
const csv = alsCsv(["Text"], [['Er sagte "hallo"; dann ging er']])
expect(csv).toContain('"Er sagte ""hallo""; dann ging er"')
})
})
describe("Kalenderdatei", () => {
it("erzeugt einen ganztägigen Termin mit Erinnerung zwei Tage vorher", () => {
const ics = kalenderDatei("Ölwechsel", "2027-03-14", "laut Prognose")
expect(ics).toContain("BEGIN:VCALENDAR")
expect(ics).toContain("DTSTART;VALUE=DATE:20270314")
expect(ics).toContain("SUMMARY:Ölwechsel")
expect(ics).toContain("TRIGGER:-P2D")
expect(ics.trimEnd().endsWith("END:VCALENDAR")).toBe(true)
})
it("schützt Sonderzeichen im Titel", () => {
expect(kalenderDatei("Service; Öl, Filter", "2027-01-01")).toContain(
"SUMMARY:Service\\; Öl\\, Filter",
)
})
})