Files
testing-tool/src/utils/__tests__/useContextMenuData.test.ts
T
LingandRX a5d86a92c2 develop (#67)
feat: 升级项目 Logo、完善 toast 提示及优化工程化配置

### Features
- **logo**: 替换全套项目图标,更新 wxt.config.ts 浏览器扩展图标配置。

### Bug Fixes
- **ui/toast**: 引入 <Toaster> 组件修复 toast 无法显示问题,并调整位置为正下方。
- **i18n**: 统一将 t() 函数中的 `.` 和 `:` 转换为 `_`,修复国际化文案读取失败问题。
- **context-menu**: 限制 srcUrl 检查仅在二维码图片菜单项生效,避免误判。
- **hooks**: 修复 pre-push 钩子,仅对变更文件进行 eslint 检查,并支持无 upstream 时的回退对比。
- **components**: 修复 CopyButton 动画与组件导入方式,并补齐无障碍属性与测试。
- **async**: 延迟生成二维码以正确渲染 loading 状态,并适配相关异步测试。

### Refactor & Cleans
- **clean**: 清理未使用的组件(ToolCard)、Hook(useDebounce)及多个未使用的工具函数导出。
- **structure**: 统一移动测试文件至 __tests__ 目录,移除不必要的类型导出。

### Documentation
- **docs**: 在 AGENTS.md 中明确规范 Git Commit 必须使用中文描述。
2026-06-06 01:07:27 +08:00

214 lines
6.3 KiB
TypeScript

import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
import { renderHook, act } from '@testing-library/react';
import { useContextMenuData, saveContextMenuData } from '@/utils/useContextMenuData';
describe('useContextMenuData', () => {
beforeEach(() => {
vi.clearAllMocks();
vi.useFakeTimers();
});
afterEach(() => {
vi.useRealTimers();
});
describe('saveContextMenuData', () => {
it('应该保存数据到 storage 并添加时间戳', async () => {
const data = { featureKey: 'jwt' as const, payload: 'test-token' };
vi.setSystemTime(new Date('2024-01-01T12:00:00Z'));
await saveContextMenuData(data);
expect(chrome.storage.local.set).toHaveBeenCalledWith({
'contextMenu/pendingData': {
featureKey: 'jwt',
payload: 'test-token',
timestamp: 1704110400000,
},
});
});
it('应该正确处理不同的 featureKey', async () => {
const data = { featureKey: 'timestamp' as const, payload: '1234567890' };
await saveContextMenuData(data);
expect(chrome.storage.local.set).toHaveBeenCalledWith(
expect.objectContaining({
'contextMenu/pendingData': expect.objectContaining({
featureKey: 'timestamp',
payload: '1234567890',
}),
}),
);
});
});
describe('useContextMenuData Hook', () => {
it('当 storage 中有匹配数据时应调用 onData 回调', async () => {
const mockData = {
featureKey: 'jwt',
payload: 'test-token',
timestamp: Date.now(),
};
(chrome.storage.local.get as any).mockResolvedValue({
'contextMenu/pendingData': mockData,
});
const onData = vi.fn();
renderHook(() => useContextMenuData({ featureKey: 'jwt', onData }));
await vi.waitFor(() => {
expect(onData).toHaveBeenCalledWith('test-token');
});
});
it('当 storage 中没有数据时不应调用 onData 回调', async () => {
(chrome.storage.local.get as any).mockResolvedValue({});
const onData = vi.fn();
renderHook(() => useContextMenuData({ featureKey: 'jwt', onData }));
await vi.waitFor(() => {
expect(onData).not.toHaveBeenCalled();
});
});
it('当 featureKey 不匹配时不应调用 onData 回调', async () => {
const mockData = {
featureKey: 'timestamp',
payload: '1234567890',
timestamp: Date.now(),
};
(chrome.storage.local.get as any).mockResolvedValue({
'contextMenu/pendingData': mockData,
});
const onData = vi.fn();
renderHook(() => useContextMenuData({ featureKey: 'jwt', onData }));
await vi.waitFor(() => {
expect(onData).not.toHaveBeenCalled();
});
});
it('当数据过期时不应调用 onData 回调并删除数据', async () => {
const now = Date.now();
vi.setSystemTime(now);
const mockData = {
featureKey: 'jwt',
payload: 'test-token',
timestamp: now - 6000,
};
(chrome.storage.local.get as any).mockResolvedValue({
'contextMenu/pendingData': mockData,
});
const onData = vi.fn();
renderHook(() => useContextMenuData({ featureKey: 'jwt', onData }));
await vi.waitFor(() => {
expect(onData).not.toHaveBeenCalled();
expect(chrome.storage.local.remove).toHaveBeenCalledWith(['contextMenu/pendingData']);
});
});
it('消费数据后应删除 storage 中的数据', async () => {
const mockData = {
featureKey: 'jwt',
payload: 'test-token',
timestamp: Date.now(),
};
(chrome.storage.local.get as any).mockResolvedValue({
'contextMenu/pendingData': mockData,
});
const onData = vi.fn();
renderHook(() => useContextMenuData({ featureKey: 'jwt', onData }));
await vi.waitFor(() => {
expect(chrome.storage.local.remove).toHaveBeenCalledWith(['contextMenu/pendingData']);
});
});
it('当 storage 变化且 featureKey 匹配时应调用 onData 回调', async () => {
(chrome.storage.local.get as any).mockResolvedValue({});
const onData = vi.fn();
renderHook(() => useContextMenuData({ featureKey: 'jwt', onData }));
const storageChangeHandler = (chrome.storage.onChanged.addListener as any).mock.calls[0][0];
const mockData = {
featureKey: 'jwt',
payload: 'new-token',
timestamp: Date.now(),
};
(chrome.storage.local.get as any).mockResolvedValue({
'contextMenu/pendingData': mockData,
});
await act(async () => {
storageChangeHandler({
'contextMenu/pendingData': { newValue: mockData },
});
});
await vi.waitFor(() => {
expect(onData).toHaveBeenCalledWith('new-token');
});
});
it('当 storage 变化但 featureKey 不匹配时不应调用 onData 回调', async () => {
(chrome.storage.local.get as any).mockResolvedValue({});
const onData = vi.fn();
renderHook(() => useContextMenuData({ featureKey: 'jwt', onData }));
const storageChangeHandler = (chrome.storage.onChanged.addListener as any).mock.calls[0][0];
const mockData = {
featureKey: 'timestamp',
payload: '1234567890',
timestamp: Date.now(),
};
await act(async () => {
storageChangeHandler({
'contextMenu/pendingData': { newValue: mockData },
});
});
expect(onData).not.toHaveBeenCalled();
});
it('当 storage 变化但数据被删除时不应调用 onData 回调', async () => {
(chrome.storage.local.get as any).mockResolvedValue({});
const onData = vi.fn();
renderHook(() => useContextMenuData({ featureKey: 'jwt', onData }));
const storageChangeHandler = (chrome.storage.onChanged.addListener as any).mock.calls[0][0];
await act(async () => {
storageChangeHandler({
'contextMenu/pendingData': { newValue: null },
});
});
expect(onData).not.toHaveBeenCalled();
});
it('组件卸载时应移除 storage 变化监听器', () => {
const { unmount } = renderHook(() =>
useContextMenuData({ featureKey: 'jwt', onData: vi.fn() }),
);
unmount();
expect(chrome.storage.onChanged.removeListener).toHaveBeenCalled();
});
});
});