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

219 lines
6.2 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', () => {
// Save the original window.matchMedia
const originalMatchMedia = window.matchMedia;
beforeEach(() => {
// Emulate window.matchMedia
window.matchMedia = vi.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: vi.fn(), // Compatible with outdated API
removeListener: vi.fn(), // Compatible with outdated API
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn(),
}));
});
afterEach(() => {
// Restore the original window.matchMedia
window.matchMedia = originalMatchMedia;
vi.resetAllMocks();
});
it('should return false when media query does not match', () => {
// Set matchMedia to return 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', () => {
// Set matchMedia to return 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', () => {
// Create a simulated MediaQueryList
const mediaQueryList = {
matches: false,
media: '(min-width: 768px)',
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
};
// Emulate window.matchMedia returns our mediaQueryList
window.matchMedia = vi.fn().mockReturnValue(mediaQueryList);
const { result, rerender } = renderHook(() =>
useCustomMediaQuery({ rangeMinPx: '768px' }),
);
// The initial state is false
expect(result.current).toBe(false);
// Find the registered event handler
const eventListenerCall = mediaQueryList.addEventListener.mock.calls[0];
const eventType = eventListenerCall[0];
const handler = eventListenerCall[1];
// Confirm that the event type is'change'
expect(eventType).toBe('change');
// Simulate media query changes
mediaQueryList.matches = true;
handler();
// Render the hook again to get the updated value
rerender();
// It should be true now
expect(result.current).toBe(true);
});
});
describe('useMediaQuery', () => {
// Save the original window.matchMedia
const originalMatchMedia = window.matchMedia;
beforeEach(() => {
// Emulate window.matchMedia
window.matchMedia = vi.fn().mockImplementation(query => ({
matches: false,
media: query,
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
}));
});
afterEach(() => {
// Restore the original 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]})`,
);
});
});