cloud-drive-renamer/tests/unit/background-service-worker.test.ts
Zain 9ca7a6dfc8 feat(wkbrowser): 新增悟空网盘支持并更新相关文档配置
新增悟空网盘适配器,包含文件列表获取、重命名及 DOM 同步功能,更新国际化文案、商店描述、权限声明与测试用例
2026-08-15 20:01:55 +08:00

114 行
4.2 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest';
import { RUNTIME_MESSAGE_TYPES } from '../../src/types/runtime-message';
type RuntimeMessageListener = (
message: Record<string, unknown>,
sender: unknown,
sendResponse: (response: unknown) => void
) => boolean | void;
let runtimeMessageListener: RuntimeMessageListener | undefined;
let tabsQueryMock: ReturnType<typeof vi.fn>;
let tabsSendMessageMock: ReturnType<typeof vi.fn>;
function installChromeMock(tabs: Array<{ id?: number; url?: string }> = []): void {
runtimeMessageListener = undefined;
tabsSendMessageMock = vi.fn(() => Promise.resolve());
tabsQueryMock = vi.fn((_queryInfo, callback: (result: typeof tabs) => void) => callback(tabs));
(globalThis as typeof globalThis & { chrome: unknown }).chrome = {
runtime: {
onInstalled: { addListener: vi.fn() },
onMessage: {
addListener: vi.fn((listener: RuntimeMessageListener) => {
runtimeMessageListener = listener;
}),
},
},
storage: {
local: {
get: vi.fn(),
set: vi.fn(),
},
},
tabs: {
query: tabsQueryMock,
sendMessage: tabsSendMessageMock,
onUpdated: { addListener: vi.fn() },
},
};
}
async function loadServiceWorker(): Promise<RuntimeMessageListener> {
await import('../../src/background/service-worker');
if (!runtimeMessageListener) throw new Error('runtime message listener was not registered');
return runtimeMessageListener;
}
describe('background service worker', () => {
beforeEach(() => {
vi.resetModules();
vi.restoreAllMocks();
vi.useRealTimers();
});
it('broadcasts language changes to every supported cloud drive UI domain', async () => {
installChromeMock([
{ id: 1, url: 'https://pan.quark.cn/list' },
{ id: 2, url: 'https://www.aliyundrive.com/drive' },
{ id: 3, url: 'https://www.alipan.com/drive' },
{ id: 4, url: 'https://pan.baidu.com/disk/main' },
{ id: 5, url: 'https://drive.uc.cn/list' },
{ id: 6, url: 'https://pan.uc.cn/list' },
{ id: 7, url: 'https://115.com/' },
{ id: 8, url: 'https://yun.123pan.cn/' },
{ id: 9, url: 'https://yun.139.com/w/#/main' },
{ id: 10, url: 'https://cloud.189.cn/web/main' },
{ id: 11, url: 'https://pan.xunlei.com/' },
{ id: 12, url: 'https://pc.woozooo.com/mydisk.php' },
{ id: 13, url: 'https://www.guangyapan.com/#/home/all' },
{ id: 14, url: 'https://pan.wkbrowser.com/main?category=all' },
{ id: 15, url: 'https://api.wkbrowser.com/netdisk/user_file/filter_file' },
{ id: 16, url: 'https://example.com/' },
]);
const listener = await loadServiceWorker();
const sendResponse = vi.fn();
listener({ type: 'LANGUAGE_CHANGED' }, {}, sendResponse);
expect(sendResponse).toHaveBeenCalledWith({ success: true });
expect(tabsQueryMock).toHaveBeenCalledWith({}, expect.any(Function));
expect(tabsSendMessageMock.mock.calls.map((call) => call[0])).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]);
});
it('aborts GuangyaPan background API requests when the runtime timeout elapses', async () => {
vi.useFakeTimers();
installChromeMock();
const fetchMock = vi.fn((_url: string, init?: RequestInit) => new Promise((_resolve, reject) => {
init?.signal?.addEventListener('abort', () => reject(new DOMException('Aborted', 'AbortError')));
}));
vi.stubGlobal('fetch', fetchMock);
const listener = await loadServiceWorker();
const sendResponse = vi.fn();
const handled = listener({
type: RUNTIME_MESSAGE_TYPES.GUANGYAPAN_API_REQUEST,
requestId: 'req-timeout',
method: 'POST',
url: 'https://api.guangyapan.com/userres/v1/file/get_file_list',
headers: { 'Content-Type': 'application/json' },
body: { pageSize: 100 },
timeout: 25,
}, {}, sendResponse);
expect(handled).toBe(true);
expect(fetchMock).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({ signal: expect.any(AbortSignal) }));
expect(sendResponse).not.toHaveBeenCalled();
await vi.advanceTimersByTimeAsync(25);
expect(sendResponse).toHaveBeenCalledWith({ success: false, error: 'AbortError: Aborted', status: 0 });
vi.useRealTimers();
});
});