Files
testing-tool/utils/__tests__/useContextMenuData.test.ts
T
雨霖铃 3b98c9438e feat: 实现 React 页面层右键菜单数据联动
- 新增 useContextMenuData Hook 处理右键菜单数据传递
- 重构 Jwt/Base64Converter/TextStatistics/QrCode 页面接收右键数据
- RouterProvider 支持从 URL 参数解析右键菜单数据
- 添加 contextMenu/pendingData 存储键到 StorageSchema
- 添加 useContextMenuData 单元测试(12个测试用例)
2026-05-20 20:51:11 +08:00

226 lines
6.6 KiB
TypeScript

import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
import { renderHook, act } from '@testing-library/react';
import {
useContextMenuData,
saveContextMenuData,
clearContextMenuData,
} 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('clearContextMenuData', () => {
it('应该从 storage 中删除数据', async () => {
await clearContextMenuData();
expect(chrome.storage.local.remove).toHaveBeenCalledWith(['contextMenu/pendingData']);
});
});
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();
});
});
});