8d752e0bd8
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
64 lines
2.5 KiB
TypeScript
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)
|
|
})
|
|
})
|