import { describe, expect, it } from 'vitest'; import { StorageQuotaError } from '../infrastructure/db'; import { entityStorageErrorNotification, storageErrorNotification } from './storage-errors'; describe('storageErrorNotification', () => { describe('a failure the user can fix (storage full)', () => { it('tells them the step to take and carries no diagnostic detail', () => { const n = storageErrorNotification('save', new StorageQuotaError()); expect(n.kind).toBe('error'); expect(n.title).toBe('Storage full'); expect(n.message.toLowerCase()).toContain('delete'); // Nothing to report — the fix is in the user's hands. expect(n.detail).toBeUndefined(); }); }); describe('a failure the user cannot fix', () => { it('explains a load failure and attaches a reportable diagnostic', () => { const n = storageErrorNotification('load', new DOMException('blocked', 'SecurityError')); expect(n.kind).toBe('error'); expect(n.title).toBe("Couldn't open your library"); expect(n.detail).toBeDefined(); // The diagnostic names the operation and the underlying error. expect(n.detail).toContain('load'); expect(n.detail).toContain('SecurityError'); expect(n.detail).toContain('blocked'); }); it('uses the right title for a failed save vs delete and includes detail', () => { const save = storageErrorNotification('save', new Error('boom')); expect(save.title).toBe("Couldn't save your changes"); expect(save.detail).toContain('save failed'); expect(save.detail).toContain('boom'); const del = storageErrorNotification('delete', new Error('boom')); expect(del.title).toBe("Couldn't delete the snippet"); expect(del.detail).toContain('delete failed'); }); it('describes a non-Error throw without crashing', () => { const n = storageErrorNotification('save', 'weird string'); expect(n.detail).toContain('weird string'); }); }); }); describe('entityStorageErrorNotification', () => { it('names the entity in the title, message, and diagnostic', () => { const save = entityStorageErrorNotification('dataset', 'save', new Error('boom')); expect(save.kind).toBe('error'); expect(save.title).toBe("Couldn't save the dataset"); expect(save.message).toContain('dataset change'); expect(save.detail).toBe('Dataset save failed: Error: boom'); // capitalized noun in trace const del = entityStorageErrorNotification('font', 'delete', new Error('boom')); expect(del.title).toBe("Couldn't delete the font"); expect(del.detail).toContain('Font delete failed'); }); it('detects a quota failure for any entity tier (not just snippets)', () => { const n = entityStorageErrorNotification('dataset', 'save', new StorageQuotaError()); expect(n.title).toBe('Storage full'); expect(n.message.toLowerCase()).toContain('dataset'); expect(n.message.toLowerCase()).toContain('delete'); expect(n.detail).toBeUndefined(); // nothing to report — the fix is in the message }); it('frames a load failure with the entity noun, not "snippets"', () => { const n = entityStorageErrorNotification( 'dataset', 'load', new DOMException('blocked', 'SecurityError'), ); expect(n.title).toBe("Couldn't open your datasets"); expect(n.message).toContain('datasets'); expect(n.message).not.toContain('snippet'); expect(n.detail).toContain('load'); expect(n.detail).toContain('SecurityError'); }); it('describes a non-Error throw without crashing', () => { const n = entityStorageErrorNotification('theme', 'save', 'weird string'); expect(n.detail).toBe('weird string'); }); });