feat(composer): persist text + reply target per chat across restarts
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import {
|
||||
__resetForTests,
|
||||
clearDraft,
|
||||
getDraftSync,
|
||||
hasDraft,
|
||||
hydrateDrafts,
|
||||
setDraft,
|
||||
} from './composerDraftStore';
|
||||
|
||||
const sqlExecuteMock = vi.fn().mockResolvedValue(undefined);
|
||||
const sqlSelectMock = vi.fn().mockResolvedValue([]);
|
||||
const sqlLoadMock = vi.fn().mockResolvedValue('mock-handle');
|
||||
|
||||
vi.stubGlobal('window', {
|
||||
electronAPI: {
|
||||
platform: 'electron-chatapp-v1',
|
||||
sqlLoad: sqlLoadMock,
|
||||
sqlExecute: sqlExecuteMock,
|
||||
sqlSelect: sqlSelectMock,
|
||||
},
|
||||
});
|
||||
|
||||
describe('composerDraftStore', () => {
|
||||
beforeEach(() => {
|
||||
sqlExecuteMock.mockClear();
|
||||
sqlSelectMock.mockClear();
|
||||
sqlLoadMock.mockClear();
|
||||
__resetForTests();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
__resetForTests();
|
||||
});
|
||||
|
||||
it('returns null for an unknown conversation', () => {
|
||||
expect(getDraftSync('unknown')).toBeNull();
|
||||
expect(hasDraft('unknown')).toBe(false);
|
||||
});
|
||||
|
||||
it('stores and returns a draft synchronously after set', () => {
|
||||
setDraft('a', { text: 'hi', replyToId: null });
|
||||
const draft = getDraftSync('a');
|
||||
expect(draft).not.toBeNull();
|
||||
expect(draft?.text).toBe('hi');
|
||||
expect(draft?.replyToId).toBeNull();
|
||||
expect(hasDraft('a')).toBe(true);
|
||||
});
|
||||
|
||||
it('isolates drafts per conversation', () => {
|
||||
setDraft('a', { text: 'one', replyToId: null });
|
||||
setDraft('b', { text: 'two', replyToId: 'msg-9' });
|
||||
expect(getDraftSync('a')?.text).toBe('one');
|
||||
expect(getDraftSync('b')?.replyToId).toBe('msg-9');
|
||||
});
|
||||
|
||||
it('clearDraft removes the draft from memory', () => {
|
||||
setDraft('a', { text: 'one', replyToId: null });
|
||||
clearDraft('a');
|
||||
expect(getDraftSync('a')).toBeNull();
|
||||
expect(hasDraft('a')).toBe(false);
|
||||
});
|
||||
|
||||
it('treats an empty-string text + null reply as "no draft"', () => {
|
||||
setDraft('a', { text: '', replyToId: null });
|
||||
expect(getDraftSync('a')).toBeNull();
|
||||
expect(hasDraft('a')).toBe(false);
|
||||
});
|
||||
|
||||
it('hydrateDrafts populates the in-memory map from SQLite rows', async () => {
|
||||
sqlSelectMock.mockResolvedValueOnce([
|
||||
{ conversation_id: 'a', text: 'persisted', reply_to_id: 'msg-1', updated_at: '2026-05-17T00:00:00Z' },
|
||||
]);
|
||||
await hydrateDrafts();
|
||||
expect(getDraftSync('a')?.text).toBe('persisted');
|
||||
expect(getDraftSync('a')?.replyToId).toBe('msg-1');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user