feat: optimize dashboard/search UX and simplify extension architecture

Redesign Dashboard with compact tool grid and recently used tools
Improve TopBar search UX with Cmd/Ctrl+K shortcut and better history navigation
Reorganize project structure into src/
Migrate i18n from react-i18next to chrome.i18n
Remove runtime language switch and settings page
Remove HTML/Markdown conversion tools
Clean up unused code, dead animations, redundant comments, and imports
Improve component consistency with shadcn/ui patterns
Replace hardcoded strings/colors with i18n tokens and theme tokens
Add comprehensive project documentation and coding standards
Fix CI artifact upload workflow and multiple TypeScript/test issues

Includes various refactors, UI polish, i18n cleanup, CI improvements,
and maintenance updates across the codebase.
This commit is contained in:
LingandRX
2026-05-28 22:39:25 +08:00
committed by GitHub
parent d4c29a1bf4
commit 945780def8
200 changed files with 1557 additions and 4380 deletions
+169
View File
@@ -0,0 +1,169 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { act, renderHook, waitFor } from '@testing-library/react';
import { useStorageState } from '@/utils/useStorageState';
import { storageUtil } from '@/utils/chromeStorage';
import type { JsonToolsPageMode } from '@/types/storage';
// Mock storageUtil
vi.mock('@/utils/chromeStorage', () => ({
storageUtil: {
get: vi.fn(),
set: vi.fn(() => Promise.resolve()),
},
}));
describe('useStorageState', () => {
beforeEach(() => {
vi.clearAllMocks();
localStorage.clear();
});
it('应该使用默认值初始化', () => {
(storageUtil.get as any).mockImplementation((_key: string, defaultValue: any) =>
Promise.resolve(defaultValue),
);
const { result } = renderHook(() => useStorageState('qrCode/urlExpanded', true));
// 初始值应为默认值(无快照时)
expect(result.current[0]).toBe(true);
});
it('应该从 localStorage 快照同步恢复初始值', () => {
localStorage.setItem('snapshot/qrCode/urlExpanded', JSON.stringify(false));
(storageUtil.get as any).mockImplementation((_key: string, defaultValue: any) =>
Promise.resolve(defaultValue),
);
const { result } = renderHook(() => useStorageState('qrCode/urlExpanded', true));
// 初始值应从快照恢复,而非默认值
expect(result.current[0]).toBe(false);
});
it('应该从 Chrome Storage 异步加载并覆盖初始值', async () => {
(storageUtil.get as any).mockImplementation((_key: string, _defaultValue: any) =>
Promise.resolve(false),
);
const { result } = renderHook(() => useStorageState('qrCode/urlExpanded', true));
// 初始值为默认值(无快照)
expect(result.current[0]).toBe(true);
await waitFor(() => {
// 异步加载后应覆盖为存储值
expect(result.current[0]).toBe(false);
expect(result.current[2]).toBe(true);
});
});
it('状态变化时应该保存到 Chrome Storage 和 localStorage 快照', async () => {
(storageUtil.get as any).mockImplementation((_key: string, defaultValue: any) =>
Promise.resolve(defaultValue),
);
const { result } = renderHook(() => useStorageState('qrCode/urlExpanded', true));
await waitFor(() => {
expect(result.current[2]).toBe(true);
});
await act(async () => {
result.current[1](false);
});
expect(result.current[0]).toBe(false);
expect(storageUtil.set).toHaveBeenCalledWith('qrCode/urlExpanded', false);
expect(localStorage.getItem('snapshot/qrCode/urlExpanded')).toBe(JSON.stringify(false));
});
it('应该支持验证器 - 合法值通过', async () => {
const validator = (val: unknown): val is JsonToolsPageMode =>
typeof val === 'string' &&
(['diff', 'format', 'yaml', 'toml', 'minify'] as string[]).includes(val);
(storageUtil.get as any).mockImplementation(() => Promise.resolve('yaml'));
const { result } = renderHook(() => useStorageState('jsonTools/pageMode', 'diff', validator));
await waitFor(() => {
expect(result.current[0]).toBe('yaml');
});
});
it('应该支持验证器 - 非法值回退到默认值', async () => {
const validator = (val: unknown): val is JsonToolsPageMode =>
typeof val === 'string' &&
(['diff', 'format', 'yaml', 'toml', 'minify'] as string[]).includes(val);
(storageUtil.get as any).mockImplementation(() => Promise.resolve('invalidMode'));
const { result } = renderHook(() => useStorageState('jsonTools/pageMode', 'diff', validator));
await waitFor(() => {
// 非法值应回退到默认值
expect(result.current[0]).toBe('diff');
});
});
it('快照中的非法值应被验证器拒绝,回退到默认值', () => {
const validator = (val: unknown): val is JsonToolsPageMode =>
typeof val === 'string' &&
(['diff', 'format', 'yaml', 'toml', 'minify'] as string[]).includes(val);
// 在 localStorage 中存入一个非法值
localStorage.setItem('snapshot/jsonTools/pageMode', JSON.stringify('invalidMode'));
(storageUtil.get as any).mockImplementation((_key: string, defaultValue: any) =>
Promise.resolve(defaultValue),
);
const { result } = renderHook(() => useStorageState('jsonTools/pageMode', 'diff', validator));
// 快照中的非法值应被拒绝,使用默认值
expect(result.current[0]).toBe('diff');
});
it('快照中的合法值应被验证器接受', () => {
const validator = (val: unknown): val is JsonToolsPageMode =>
typeof val === 'string' &&
(['diff', 'format', 'yaml', 'toml', 'minify'] as string[]).includes(val);
// 在 localStorage 中存入一个合法值
localStorage.setItem('snapshot/jsonTools/pageMode', JSON.stringify('minify'));
(storageUtil.get as any).mockImplementation((_key: string, defaultValue: any) =>
Promise.resolve(defaultValue),
);
const { result } = renderHook(() => useStorageState('jsonTools/pageMode', 'diff', validator));
// 快照中的合法值应被接受
expect(result.current[0]).toBe('minify');
});
it('快照中损坏的 JSON 应被忽略,回退到默认值', () => {
localStorage.setItem('snapshot/qrCode/urlExpanded', 'not-valid-json{');
(storageUtil.get as any).mockImplementation((_key: string, defaultValue: any) =>
Promise.resolve(defaultValue),
);
const { result } = renderHook(() => useStorageState('qrCode/urlExpanded', true));
// 损坏的快照应被忽略
expect(result.current[0]).toBe(true);
});
it('isInitialized 在异步加载完成前应为 false', () => {
// 让 Chrome Storage 永远不 resolve
(storageUtil.get as any).mockImplementation(() => new Promise(() => {}));
const { result } = renderHook(() => useStorageState('qrCode/urlExpanded', true));
expect(result.current[2]).toBe(false);
});
});