217 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
			
		
		
	
	
			217 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
/*
 | 
						|
 * Copyright 2025 coze-dev Authors
 | 
						|
 *
 | 
						|
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
 * you may not use this file except in compliance with the License.
 | 
						|
 * You may obtain a copy of the License at
 | 
						|
 *
 | 
						|
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 *
 | 
						|
 * Unless required by applicable law or agreed to in writing, software
 | 
						|
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
 * See the License for the specific language governing permissions and
 | 
						|
 * limitations under the License.
 | 
						|
 */
 | 
						|
 
 | 
						|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
 | 
						|
import { renderHook } from '@testing-library/react';
 | 
						|
 | 
						|
import {
 | 
						|
  useMediaQuery,
 | 
						|
  useCustomMediaQuery,
 | 
						|
} from '../../src/hooks/media-query';
 | 
						|
import {
 | 
						|
  ScreenRange,
 | 
						|
  SCREENS_TOKENS,
 | 
						|
  SCREENS_TOKENS_2,
 | 
						|
} from '../../src/constant';
 | 
						|
 | 
						|
describe('useCustomMediaQuery', () => {
 | 
						|
  // 保存原始的 window.matchMedia
 | 
						|
  const originalMatchMedia = window.matchMedia;
 | 
						|
 | 
						|
  beforeEach(() => {
 | 
						|
    // 模拟 window.matchMedia
 | 
						|
    window.matchMedia = vi.fn().mockImplementation(query => ({
 | 
						|
      matches: false,
 | 
						|
      media: query,
 | 
						|
      onchange: null,
 | 
						|
      addListener: vi.fn(), // 兼容旧版API
 | 
						|
      removeListener: vi.fn(), // 兼容旧版API
 | 
						|
      addEventListener: vi.fn(),
 | 
						|
      removeEventListener: vi.fn(),
 | 
						|
      dispatchEvent: vi.fn(),
 | 
						|
    }));
 | 
						|
  });
 | 
						|
 | 
						|
  afterEach(() => {
 | 
						|
    // 恢复原始的 window.matchMedia
 | 
						|
    window.matchMedia = originalMatchMedia;
 | 
						|
    vi.resetAllMocks();
 | 
						|
  });
 | 
						|
 | 
						|
  it('should return false when media query does not match', () => {
 | 
						|
    // 设置 matchMedia 返回 false
 | 
						|
    window.matchMedia = vi.fn().mockImplementation(query => ({
 | 
						|
      matches: false,
 | 
						|
      media: query,
 | 
						|
      addEventListener: vi.fn(),
 | 
						|
      removeEventListener: vi.fn(),
 | 
						|
    }));
 | 
						|
 | 
						|
    const { result } = renderHook(() =>
 | 
						|
      useCustomMediaQuery({ rangeMinPx: '768px', rangeMaxPx: '1200px' }),
 | 
						|
    );
 | 
						|
 | 
						|
    expect(result.current).toBe(false);
 | 
						|
    expect(window.matchMedia).toHaveBeenCalledWith(
 | 
						|
      '(min-width: 768px) and (max-width: 1200px)',
 | 
						|
    );
 | 
						|
  });
 | 
						|
 | 
						|
  it('should return true when media query matches', () => {
 | 
						|
    // 设置 matchMedia 返回 true
 | 
						|
    window.matchMedia = vi.fn().mockImplementation(query => ({
 | 
						|
      matches: true,
 | 
						|
      media: query,
 | 
						|
      addEventListener: vi.fn(),
 | 
						|
      removeEventListener: vi.fn(),
 | 
						|
    }));
 | 
						|
 | 
						|
    const { result } = renderHook(() =>
 | 
						|
      useCustomMediaQuery({ rangeMinPx: '768px', rangeMaxPx: '1200px' }),
 | 
						|
    );
 | 
						|
 | 
						|
    expect(result.current).toBe(true);
 | 
						|
    expect(window.matchMedia).toHaveBeenCalledWith(
 | 
						|
      '(min-width: 768px) and (max-width: 1200px)',
 | 
						|
    );
 | 
						|
  });
 | 
						|
 | 
						|
  it('should handle only min width', () => {
 | 
						|
    window.matchMedia = vi.fn().mockImplementation(query => ({
 | 
						|
      matches: true,
 | 
						|
      media: query,
 | 
						|
      addEventListener: vi.fn(),
 | 
						|
      removeEventListener: vi.fn(),
 | 
						|
    }));
 | 
						|
 | 
						|
    const { result } = renderHook(() =>
 | 
						|
      useCustomMediaQuery({ rangeMinPx: '768px' }),
 | 
						|
    );
 | 
						|
 | 
						|
    expect(result.current).toBe(true);
 | 
						|
    expect(window.matchMedia).toHaveBeenCalledWith('(min-width: 768px)');
 | 
						|
  });
 | 
						|
 | 
						|
  it('should handle only max width', () => {
 | 
						|
    window.matchMedia = vi.fn().mockImplementation(query => ({
 | 
						|
      matches: true,
 | 
						|
      media: query,
 | 
						|
      addEventListener: vi.fn(),
 | 
						|
      removeEventListener: vi.fn(),
 | 
						|
    }));
 | 
						|
 | 
						|
    const { result } = renderHook(() =>
 | 
						|
      useCustomMediaQuery({ rangeMaxPx: '1200px' }),
 | 
						|
    );
 | 
						|
 | 
						|
    expect(result.current).toBe(true);
 | 
						|
    expect(window.matchMedia).toHaveBeenCalledWith('(max-width: 1200px)');
 | 
						|
  });
 | 
						|
 | 
						|
  it('should update when media query changes', () => {
 | 
						|
    // 创建一个模拟的 MediaQueryList
 | 
						|
    const mediaQueryList = {
 | 
						|
      matches: false,
 | 
						|
      media: '(min-width: 768px)',
 | 
						|
      addEventListener: vi.fn(),
 | 
						|
      removeEventListener: vi.fn(),
 | 
						|
    };
 | 
						|
 | 
						|
    // 模拟 window.matchMedia 返回我们的 mediaQueryList
 | 
						|
    window.matchMedia = vi.fn().mockReturnValue(mediaQueryList);
 | 
						|
 | 
						|
    const { result, rerender } = renderHook(() =>
 | 
						|
      useCustomMediaQuery({ rangeMinPx: '768px' }),
 | 
						|
    );
 | 
						|
 | 
						|
    // 初始状态是 false
 | 
						|
    expect(result.current).toBe(false);
 | 
						|
 | 
						|
    // 找到注册的事件处理函数
 | 
						|
    const eventListenerCall = mediaQueryList.addEventListener.mock.calls[0];
 | 
						|
    const eventType = eventListenerCall[0];
 | 
						|
    const handler = eventListenerCall[1];
 | 
						|
 | 
						|
    // 确认事件类型是 'change'
 | 
						|
    expect(eventType).toBe('change');
 | 
						|
 | 
						|
    // 模拟媒体查询变化
 | 
						|
    mediaQueryList.matches = true;
 | 
						|
    handler();
 | 
						|
 | 
						|
    // 重新渲染钩子以获取更新后的值
 | 
						|
    rerender();
 | 
						|
 | 
						|
    // 现在应该是 true
 | 
						|
    expect(result.current).toBe(true);
 | 
						|
  });
 | 
						|
});
 | 
						|
 | 
						|
describe('useMediaQuery', () => {
 | 
						|
  // 保存原始的 window.matchMedia
 | 
						|
  const originalMatchMedia = window.matchMedia;
 | 
						|
 | 
						|
  beforeEach(() => {
 | 
						|
    // 模拟 window.matchMedia
 | 
						|
    window.matchMedia = vi.fn().mockImplementation(query => ({
 | 
						|
      matches: false,
 | 
						|
      media: query,
 | 
						|
      addEventListener: vi.fn(),
 | 
						|
      removeEventListener: vi.fn(),
 | 
						|
    }));
 | 
						|
  });
 | 
						|
 | 
						|
  afterEach(() => {
 | 
						|
    // 恢复原始的 window.matchMedia
 | 
						|
    window.matchMedia = originalMatchMedia;
 | 
						|
    vi.resetAllMocks();
 | 
						|
  });
 | 
						|
 | 
						|
  it('should use correct screen tokens for min and max ranges', () => {
 | 
						|
    renderHook(() =>
 | 
						|
      useMediaQuery({ rangeMin: ScreenRange.MD, rangeMax: ScreenRange.LG }),
 | 
						|
    );
 | 
						|
 | 
						|
    expect(window.matchMedia).toHaveBeenCalledWith(
 | 
						|
      `(min-width: ${SCREENS_TOKENS[ScreenRange.MD]}) and (max-width: ${SCREENS_TOKENS[ScreenRange.LG]})`,
 | 
						|
    );
 | 
						|
  });
 | 
						|
 | 
						|
  it('should handle only min range', () => {
 | 
						|
    renderHook(() => useMediaQuery({ rangeMin: ScreenRange.MD }));
 | 
						|
 | 
						|
    expect(window.matchMedia).toHaveBeenCalledWith(
 | 
						|
      `(min-width: ${SCREENS_TOKENS[ScreenRange.MD]})`,
 | 
						|
    );
 | 
						|
  });
 | 
						|
 | 
						|
  it('should handle only max range', () => {
 | 
						|
    renderHook(() => useMediaQuery({ rangeMax: ScreenRange.LG }));
 | 
						|
 | 
						|
    expect(window.matchMedia).toHaveBeenCalledWith(
 | 
						|
      `(max-width: ${SCREENS_TOKENS[ScreenRange.LG]})`,
 | 
						|
    );
 | 
						|
  });
 | 
						|
 | 
						|
  it('should use tokens from SCREENS_TOKENS_2 when available', () => {
 | 
						|
    renderHook(() => useMediaQuery({ rangeMin: ScreenRange.XL1_5 }));
 | 
						|
 | 
						|
    expect(window.matchMedia).toHaveBeenCalledWith(
 | 
						|
      `(min-width: ${SCREENS_TOKENS_2[ScreenRange.XL1_5]})`,
 | 
						|
    );
 | 
						|
  });
 | 
						|
});
 |