Files
homelable/frontend/src/utils/__tests__/autosaveSettings.test.ts
T
Pouzor 8d752e0bd8 fix(canvas): gate autosave on canvas provenance, not selection
The previous guard compared the pinned design against the live
activeDesignId (the selection). On a design switch the selection flips
synchronously while the new canvas loads asynchronously, so the
on-screen nodes briefly still belong to the previous design — arming the
timer with the newly-selected id would save those stale nodes under the
wrong design. Track the design the canvas was actually loaded as
(loadedDesignId provenance) and gate autosave on that instead: pin it
when the timer is armed and re-check the live provenance at fire time,
skipping the save if a different canvas has since loaded.

Also validate the persisted `enabled` flag by type (mirror of `delay`),
and add the matching regression tests (provenance switch skip, enabled
type validation).

ha-relevant: maybe
2026-07-17 11:43:42 +02:00

64 lines
2.5 KiB
TypeScript

import { describe, it, expect, beforeEach, vi } from 'vitest'
import {
DEFAULT_AUTOSAVE_SETTINGS,
readAutosaveSettings,
writeAutosaveSettings,
subscribeAutosaveSettings,
} from '../autosaveSettings'
describe('autosaveSettings', () => {
beforeEach(() => {
localStorage.clear()
})
it('defaults to disabled with a 5s delay', () => {
expect(DEFAULT_AUTOSAVE_SETTINGS).toEqual({ enabled: false, delay: 5 })
})
it('returns defaults when nothing stored', () => {
expect(readAutosaveSettings()).toEqual(DEFAULT_AUTOSAVE_SETTINGS)
})
it('roundtrips through localStorage', () => {
writeAutosaveSettings({ enabled: true, delay: 30 })
expect(readAutosaveSettings()).toEqual({ enabled: true, delay: 30 })
})
it('falls back to defaults when stored value is corrupted', () => {
localStorage.setItem('homelable.autosave', '{not json')
expect(readAutosaveSettings()).toEqual(DEFAULT_AUTOSAVE_SETTINGS)
})
it('fills missing fields from defaults', () => {
localStorage.setItem('homelable.autosave', JSON.stringify({ enabled: true }))
expect(readAutosaveSettings()).toEqual({ enabled: true, delay: DEFAULT_AUTOSAVE_SETTINGS.delay })
})
it('rejects a non-positive delay and falls back to default', () => {
localStorage.setItem('homelable.autosave', JSON.stringify({ enabled: true, delay: 0 }))
expect(readAutosaveSettings().delay).toBe(DEFAULT_AUTOSAVE_SETTINGS.delay)
localStorage.setItem('homelable.autosave', JSON.stringify({ enabled: true, delay: -10 }))
expect(readAutosaveSettings().delay).toBe(DEFAULT_AUTOSAVE_SETTINGS.delay)
})
it('rejects a non-numeric delay and falls back to default', () => {
localStorage.setItem('homelable.autosave', JSON.stringify({ enabled: true, delay: 'soon' }))
expect(readAutosaveSettings().delay).toBe(DEFAULT_AUTOSAVE_SETTINGS.delay)
})
it('rejects a non-boolean enabled and falls back to default', () => {
localStorage.setItem('homelable.autosave', JSON.stringify({ enabled: 'yes', delay: 10 }))
expect(readAutosaveSettings().enabled).toBe(DEFAULT_AUTOSAVE_SETTINGS.enabled)
})
it('notifies subscribers on write and stops after unsubscribe', () => {
const listener = vi.fn()
const unsubscribe = subscribeAutosaveSettings(listener)
writeAutosaveSettings({ enabled: true, delay: 10 })
expect(listener).toHaveBeenCalledWith({ enabled: true, delay: 10 })
unsubscribe()
writeAutosaveSettings({ enabled: false, delay: 3 })
expect(listener).toHaveBeenCalledTimes(1)
})
})