coze-studio/frontend/packages/arch/responsive-kit/__tests__/hooks/media-query.test.ts

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]})`,
);
});
});