mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2026-06-26 01:58:27 +08:00
151 lines
4.7 KiB
TypeScript
151 lines
4.7 KiB
TypeScript
/*
|
|
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
|
*
|
|
* Copyright (C) 2025 Tencent.
|
|
*/
|
|
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
|
|
|
import { idbDelete, idbGet, idbSet, isIndexedDBSupported, openIndexedDB } from '@editor/utils/indexed-db';
|
|
|
|
type StoreRecord = Map<IDBValidKey, unknown>;
|
|
|
|
interface FakeDb {
|
|
version: number;
|
|
stores: Map<string, StoreRecord>;
|
|
}
|
|
|
|
const fakeDbs = new Map<string, FakeDb>();
|
|
|
|
const createRequest = <T>(result: T, error: DOMException | null = null) => {
|
|
const request = {
|
|
result,
|
|
error,
|
|
onsuccess: null as null | (() => void),
|
|
onerror: null as null | (() => void),
|
|
onupgradeneeded: null as null | (() => void),
|
|
};
|
|
queueMicrotask(() => {
|
|
if (error) {
|
|
request.onerror?.();
|
|
} else {
|
|
request.onsuccess?.();
|
|
}
|
|
});
|
|
return request;
|
|
};
|
|
|
|
const installFakeIndexedDB = () => {
|
|
fakeDbs.clear();
|
|
const indexedDB = {
|
|
open: vi.fn((name: string, version?: number) => {
|
|
const existing = fakeDbs.get(name);
|
|
const nextVersion = version ?? existing?.version ?? 1;
|
|
const db: FakeDb = existing ?? { version: nextVersion, stores: new Map() };
|
|
if (version !== undefined && version > (existing?.version ?? 0)) {
|
|
db.version = version;
|
|
}
|
|
fakeDbs.set(name, db);
|
|
|
|
const idbDatabase = {
|
|
name,
|
|
version: db.version,
|
|
objectStoreNames: {
|
|
contains: (storeName: string) => db.stores.has(storeName),
|
|
},
|
|
createObjectStore: (storeName: string) => {
|
|
if (!db.stores.has(storeName)) {
|
|
db.stores.set(storeName, new Map());
|
|
}
|
|
return {};
|
|
},
|
|
close: vi.fn(),
|
|
transaction: (storeName: string, mode: IDBTransactionMode) => {
|
|
const store = db.stores.get(storeName) ?? new Map<IDBValidKey, unknown>();
|
|
if (!db.stores.has(storeName)) {
|
|
db.stores.set(storeName, store);
|
|
}
|
|
const tx = {
|
|
error: null as DOMException | null,
|
|
objectStore: () => ({
|
|
put: (value: unknown, key: IDBValidKey) => {
|
|
if (mode === 'readonly') {
|
|
return createRequest(undefined, new DOMException('readonly'));
|
|
}
|
|
store.set(key, value);
|
|
return createRequest(undefined);
|
|
},
|
|
get: (key: IDBValidKey) => createRequest(store.get(key)),
|
|
delete: (key: IDBValidKey) => {
|
|
if (mode === 'readonly') {
|
|
return createRequest(undefined, new DOMException('readonly'));
|
|
}
|
|
store.delete(key);
|
|
return createRequest(undefined);
|
|
},
|
|
}),
|
|
oncomplete: null as null | (() => void),
|
|
onabort: null as null | (() => void),
|
|
onerror: null as null | (() => void),
|
|
};
|
|
queueMicrotask(() => tx.oncomplete?.());
|
|
return tx;
|
|
},
|
|
};
|
|
|
|
const request = createRequest(idbDatabase);
|
|
request.onupgradeneeded = () => {
|
|
if (!db.stores.has('__placeholder__')) {
|
|
// no-op: upgrade hook exists for API compatibility
|
|
}
|
|
};
|
|
queueMicrotask(() => request.onupgradeneeded?.());
|
|
return request;
|
|
}),
|
|
};
|
|
|
|
vi.stubGlobal('indexedDB', indexedDB);
|
|
return indexedDB;
|
|
};
|
|
|
|
describe('indexed-db utils', () => {
|
|
beforeEach(() => {
|
|
installFakeIndexedDB();
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
test('isIndexedDBSupported 在存在 indexedDB 时返回 true', () => {
|
|
expect(isIndexedDBSupported()).toBe(true);
|
|
});
|
|
|
|
test('isIndexedDBSupported 在无 indexedDB 环境返回 false', () => {
|
|
vi.stubGlobal('indexedDB', undefined);
|
|
expect(isIndexedDBSupported()).toBe(false);
|
|
});
|
|
|
|
test('openIndexedDB 在不支持 IndexedDB 时 reject', async () => {
|
|
vi.stubGlobal('indexedDB', undefined);
|
|
await expect(openIndexedDB('db', 'store')).rejects.toThrow('当前环境不支持 IndexedDB');
|
|
});
|
|
|
|
test('openIndexedDB 在 store 不存在时会升级创建', async () => {
|
|
const db = await openIndexedDB('tmagic-test', 'history');
|
|
expect(db.objectStoreNames.contains('history')).toBe(true);
|
|
db.close();
|
|
});
|
|
|
|
test('idbSet / idbGet / idbDelete 可读写删除记录', async () => {
|
|
await idbSet('tmagic-kv', 'items', 'k1', { hello: 'world' });
|
|
await expect(idbGet('tmagic-kv', 'items', 'k1')).resolves.toEqual({ hello: 'world' });
|
|
|
|
await idbDelete('tmagic-kv', 'items', 'k1');
|
|
await expect(idbGet('tmagic-kv', 'items', 'k1')).resolves.toBeUndefined();
|
|
});
|
|
|
|
test('idbGet 读取不存在的 key 返回 undefined', async () => {
|
|
await expect(idbGet('tmagic-kv', 'items', 'missing')).resolves.toBeUndefined();
|
|
});
|
|
});
|