Offline - Alle Schreibzugriffe von API-Nutzern laufen über eine Warteschlange in IndexedDB (services/syncQueue.js): FIFO, zusammengefasste Updates, Wiederholung bei Netz/Sichtbarkeit/Backoff, 4xx wird als Fehler gemeldet. - Medien-Uploads liegen mit Bytes in der Queue, erscheinen sofort mit Marker und werden nach dem Upload durch das Server-Objekt ersetzt. - PWA-Modus mit Service Worker für die App-Hülle; API nie gecacht. npm run build erzeugt dist/pwa. - Speicherzustand im Panel: ausstehend / Fehler mit Wiederholen-Knopf, Verbindungsanzeige im Header. Event - Event entsteht erst mit dem ersten Eintrag; jede Löschung fragt nach. - Key Image als 4:3-Rahmen, Galerie mit Video, mehrere Sprachnotizen mit Notiz, Live-Pegel beim Aufnehmen, WAV-Umwandlung für lokale Aufnahmen. - Neue Felder: Zeitraum, Kategorie, Personen, Meilenstein; Stimmung als kompakte Zeile mit ausklappbarer Punktfarbe. Oberfläche - Untere Leiste: Darstellung und Zoom links, Jahres-Pille mittig, Plus rechts. - Logos und Goldpalette, Login neu, zehn Wellen-Presets mit Vorschau, Hintergrundbilder als Kacheln, Kontomenü und Einstellungen bereinigt, Escape schließt Ebenen, größere Touch-Ziele. - Alten Sync-Service und ungenutzte Legacy-Seiten entfernt. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
78 lines
2.7 KiB
JavaScript
78 lines
2.7 KiB
JavaScript
import { describe, expect, it } from 'vitest'
|
|
import {
|
|
calendarMonthIndex,
|
|
calendarYear,
|
|
compareCalendarDates,
|
|
formatCalendarDate,
|
|
parseCalendarDate
|
|
} from 'src/utils/calendarDate'
|
|
|
|
/**
|
|
* The process timezone is fixed at start-up, so a single run cannot cover
|
|
* several zones. Instead these tests state the invariant directly: whatever
|
|
* zone the runner is in, the parsed day, month and year must equal the
|
|
* digits in the string. `npm run test:tz` runs the suite under the zones
|
|
* that used to shift the date.
|
|
*/
|
|
describe('calendar dates are parsed as calendar dates, not as UTC instants (A18)', () => {
|
|
it.each([
|
|
['2026-01-01', 2026, 0, 1],
|
|
['2025-12-31', 2025, 11, 31],
|
|
['2014-08-12', 2014, 7, 12],
|
|
['2000-02-29', 2000, 1, 29]
|
|
])('%s keeps its year, month and day in %s', (value, year, month, day) => {
|
|
const date = parseCalendarDate(value)
|
|
|
|
expect(date.getFullYear()).toBe(year)
|
|
expect(date.getMonth()).toBe(month)
|
|
expect(date.getDate()).toBe(day)
|
|
})
|
|
|
|
it('exposes the same year and month index used by the timeline labels', () => {
|
|
expect(calendarYear('2026-01-01')).toBe(2026)
|
|
expect(calendarMonthIndex('2026-01-01')).toBe(0)
|
|
})
|
|
|
|
it('formats without shifting into the previous day', () => {
|
|
expect(formatCalendarDate('2026-01-01')).toBe('01. Januar 2026')
|
|
})
|
|
|
|
it('ignores a trailing time portion instead of letting it move the day', () => {
|
|
expect(calendarYear('2026-01-01T00:00:00.000Z')).toBe(2026)
|
|
expect(calendarMonthIndex('2026-01-01T00:00:00.000Z')).toBe(0)
|
|
})
|
|
|
|
it('returns null for values that are not calendar dates', () => {
|
|
expect(parseCalendarDate('')).toBeNull()
|
|
expect(parseCalendarDate(null)).toBeNull()
|
|
expect(parseCalendarDate('gestern')).toBeNull()
|
|
expect(calendarYear(undefined)).toBeNull()
|
|
})
|
|
|
|
it('leaves unparseable input untouched when formatting', () => {
|
|
expect(formatCalendarDate('gestern')).toBe('gestern')
|
|
})
|
|
|
|
it('sorts chronologically without allocating a Date', () => {
|
|
const dates = ['2026-01-01', '2014-08-12', '2025-12-31', '2025-12-30']
|
|
|
|
expect([...dates].sort(compareCalendarDates)).toEqual([
|
|
'2014-08-12', '2025-12-30', '2025-12-31', '2026-01-01'
|
|
])
|
|
expect(compareCalendarDates('2025-12-31', '2026-01-01')).toBeLessThan(0)
|
|
expect(compareCalendarDates('2026-01-01', '2026-01-01')).toBe(0)
|
|
})
|
|
|
|
it('documents the old behaviour it replaces', () => {
|
|
// This is the bug: in any zone west of UTC, the local getters of a
|
|
// UTC-parsed instant point at the day before.
|
|
const offsetMinutes = new Date('2026-01-01').getTimezoneOffset()
|
|
const oldDay = new Date('2026-01-01').getDate()
|
|
|
|
if (offsetMinutes > 0) {
|
|
expect(oldDay).toBe(31)
|
|
}
|
|
|
|
expect(parseCalendarDate('2026-01-01').getDate()).toBe(1)
|
|
})
|
|
})
|