| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103 |
- import { afterEach, vi } from 'vitest';
- import { cleanup } from '@testing-library/react';
- import i18next from 'i18next';
- import { initReactI18next } from 'react-i18next';
- import enUS from '../../../internal/web/translation/en-US.json';
- vi.mock('persian-calendar-suite', () => ({
- PersianDateTimePicker: () => null,
- }));
- if (typeof globalThis.localStorage === 'undefined') {
- const store = new Map<string, string>();
- const storage = {
- getItem: (k: string) => (store.has(k) ? store.get(k)! : null),
- setItem: (k: string, v: string) => {
- store.set(k, String(v));
- },
- removeItem: (k: string) => {
- store.delete(k);
- },
- clear: () => {
- store.clear();
- },
- key: (i: number) => Array.from(store.keys())[i] ?? null,
- get length() {
- return store.size;
- },
- } as Storage;
- Object.defineProperty(globalThis, 'localStorage', { value: storage, configurable: true });
- Object.defineProperty(globalThis, 'sessionStorage', { value: storage, configurable: true });
- }
- if (!window.matchMedia) {
- window.matchMedia = ((query: string) => ({
- matches: false,
- media: query,
- onchange: null,
- addListener: () => {},
- removeListener: () => {},
- addEventListener: () => {},
- removeEventListener: () => {},
- dispatchEvent: () => false,
- })) as unknown as typeof window.matchMedia;
- }
- if (typeof globalThis.ResizeObserver === 'undefined') {
- globalThis.ResizeObserver = class {
- observe() {}
- unobserve() {}
- disconnect() {}
- } as unknown as typeof ResizeObserver;
- }
- if (!Element.prototype.scrollIntoView) {
- Element.prototype.scrollIntoView = () => {};
- }
- // jsdom does not implement pseudo-element styles or Range geometry. Ant
- // Design and CodeMirror use these APIs for layout, so supply harmless test
- // fallbacks instead of emitting noisy "Not implemented" errors.
- const nativeGetComputedStyle = window.getComputedStyle.bind(window);
- window.getComputedStyle = ((element: Element) =>
- nativeGetComputedStyle(element)) as typeof window.getComputedStyle;
- if (!Range.prototype.getClientRects) {
- Range.prototype.getClientRects = () => [] as unknown as DOMRectList;
- }
- if (!i18next.isInitialized) {
- void i18next.use(initReactI18next).init({
- lng: 'en-US',
- fallbackLng: 'en-US',
- resources: { 'en-US': { translation: enUS } },
- interpolation: { escapeValue: false, prefix: '{', suffix: '}' },
- returnNull: false,
- });
- }
- afterEach(async () => {
- cleanup();
- document.body.innerHTML = '';
- /*
- * React 19 defers passive-effect flushes onto a macrotask (setImmediate),
- * whose callback reads `window.event`. If one is still queued when vitest
- * tears down the jsdom environment, it fires after `window` is gone and
- * throws "window is not defined". Drain a few macrotask ticks here so any
- * pending callback runs while `window` still exists. Several ticks are used
- * because a microtask resolving mid-drain (rc-trigger/AntD) can queue a new
- * one behind the first.
- */
- for (let i = 0; i < 3; i += 1) {
- await new Promise((resolve) => setTimeout(resolve, 0));
- }
- });
- import { HttpUtil, Msg } from '@/utils';
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- vi.spyOn(HttpUtil, 'post').mockResolvedValue({ success: true, obj: {} } as any);
- vi.spyOn(HttpUtil, 'get').mockImplementation(
- async (url: string) => new Msg(true, '', url.includes('/panel/api/inbounds/options') ? [] : {}),
- );
|