test: add unit tests for hooks (useBookmarks, useUpdateCheck)
- Export loadBookmarks/saveBookmarks and isNewer for testability - Add 12 tests covering bookmark persistence and semver comparison - Total: 165 tests passing
This commit is contained in:
56
src/hooks/__tests__/useBookmarks.test.ts
Normal file
56
src/hooks/__tests__/useBookmarks.test.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { loadBookmarks, saveBookmarks, type Bookmark } from '../useBookmarks';
|
||||
|
||||
const STORAGE_KEY = 'pinchchat-bookmarks';
|
||||
|
||||
// Mock localStorage
|
||||
const store: Record<string, string> = {};
|
||||
const localStorageMock = {
|
||||
getItem: vi.fn((key: string) => store[key] ?? null),
|
||||
setItem: vi.fn((key: string, value: string) => { store[key] = value; }),
|
||||
removeItem: vi.fn((key: string) => { delete store[key]; }),
|
||||
};
|
||||
|
||||
Object.defineProperty(globalThis, 'localStorage', { value: localStorageMock });
|
||||
|
||||
beforeEach(() => {
|
||||
for (const key of Object.keys(store)) delete store[key];
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('loadBookmarks', () => {
|
||||
it('returns empty array when nothing stored', () => {
|
||||
expect(loadBookmarks()).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns parsed bookmarks from localStorage', () => {
|
||||
const bookmarks: Bookmark[] = [
|
||||
{ messageId: 'msg1', sessionKey: 'sess1', preview: 'Hello', timestamp: 1000, bookmarkedAt: 2000 },
|
||||
];
|
||||
store[STORAGE_KEY] = JSON.stringify(bookmarks);
|
||||
expect(loadBookmarks()).toEqual(bookmarks);
|
||||
});
|
||||
|
||||
it('returns empty array on corrupt JSON', () => {
|
||||
store[STORAGE_KEY] = '{broken';
|
||||
expect(loadBookmarks()).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('saveBookmarks', () => {
|
||||
it('persists bookmarks to localStorage', () => {
|
||||
const bookmarks: Bookmark[] = [
|
||||
{ messageId: 'msg1', sessionKey: 'sess1', preview: 'Test', timestamp: 1000, bookmarkedAt: 2000 },
|
||||
];
|
||||
saveBookmarks(bookmarks);
|
||||
expect(JSON.parse(store[STORAGE_KEY]!)).toEqual(bookmarks);
|
||||
});
|
||||
|
||||
it('overwrites existing bookmarks', () => {
|
||||
saveBookmarks([{ messageId: 'old', sessionKey: 's', preview: '', timestamp: 0, bookmarkedAt: 0 }]);
|
||||
saveBookmarks([{ messageId: 'new', sessionKey: 's', preview: '', timestamp: 0, bookmarkedAt: 0 }]);
|
||||
const stored = JSON.parse(store[STORAGE_KEY]!);
|
||||
expect(stored).toHaveLength(1);
|
||||
expect(stored[0].messageId).toBe('new');
|
||||
});
|
||||
});
|
||||
33
src/hooks/__tests__/useUpdateCheck.test.ts
Normal file
33
src/hooks/__tests__/useUpdateCheck.test.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { isNewer } from '../useUpdateCheck';
|
||||
|
||||
describe('isNewer', () => {
|
||||
it('returns true when remote major is higher', () => {
|
||||
expect(isNewer('2.0.0', '1.0.0')).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true when remote minor is higher', () => {
|
||||
expect(isNewer('1.5.0', '1.4.0')).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true when remote patch is higher', () => {
|
||||
expect(isNewer('1.4.2', '1.4.1')).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false when versions are equal', () => {
|
||||
expect(isNewer('1.4.1', '1.4.1')).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false when remote is older', () => {
|
||||
expect(isNewer('1.3.9', '1.4.0')).toBe(false);
|
||||
});
|
||||
|
||||
it('handles missing patch segments', () => {
|
||||
expect(isNewer('1.1', '1.0.9')).toBe(true);
|
||||
});
|
||||
|
||||
it('handles large version numbers', () => {
|
||||
expect(isNewer('1.66.1', '1.66.0')).toBe(true);
|
||||
expect(isNewer('1.66.0', '1.66.1')).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -10,7 +10,7 @@ export interface Bookmark {
|
||||
bookmarkedAt: number;
|
||||
}
|
||||
|
||||
function loadBookmarks(): Bookmark[] {
|
||||
export function loadBookmarks(): Bookmark[] {
|
||||
try {
|
||||
const raw = localStorage.getItem(STORAGE_KEY);
|
||||
if (raw) return JSON.parse(raw) as Bookmark[];
|
||||
@@ -18,7 +18,7 @@ function loadBookmarks(): Bookmark[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
function saveBookmarks(bookmarks: Bookmark[]) {
|
||||
export function saveBookmarks(bookmarks: Bookmark[]) {
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(bookmarks));
|
||||
} catch { /* noop */ }
|
||||
|
||||
@@ -54,7 +54,7 @@ export function useUpdateCheck(currentVersion: string): UpdateInfo {
|
||||
}
|
||||
|
||||
/** True if remote is newer than local (semver compare) */
|
||||
function isNewer(remote: string, local: string): boolean {
|
||||
export function isNewer(remote: string, local: string): boolean {
|
||||
const r = remote.split('.').map(Number);
|
||||
const l = local.split('.').map(Number);
|
||||
for (let i = 0; i < 3; i++) {
|
||||
|
||||
Reference in New Issue
Block a user