/** * Radix UI Select组件的测试工具函数 * 用于在测试中处理Radix UI Select组件的交互 * * 注意:这个文件应该只在测试环境中使用,不要在生产代码中导入 */ import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; /** * 查找Radix UI Select组件的隐藏select元素 * Radix UI Select组件在DOM中会创建一个隐藏的select元素用于表单提交 * 这个函数帮助我们在测试中找到这个隐藏的select元素 * * @param selectButton - Select组件的触发按钮元素 * @returns 隐藏的select元素,如果找不到则返回null */ export function findHiddenSelectElement(selectButton: HTMLElement): HTMLSelectElement | null { // 在按钮的父元素或附近查找隐藏的select // 优先在按钮附近查找,如果找不到再全局查找 const hiddenSelect = selectButton.closest('div')?.querySelector('select[data-radix-select-viewport]') || selectButton.closest('div')?.querySelector('select[aria-hidden="true"]') || selectButton.closest('div')?.querySelector('select[hidden]') || document.querySelector('select[data-radix-select-viewport]') || document.querySelector('select[aria-hidden="true"]') || document.querySelector('select[hidden]'); return hiddenSelect as HTMLSelectElement | null; } /** * 选择Radix UI Select组件的选项 * 这个函数封装了查找隐藏select元素并选择选项的逻辑 * * @param selectButton - Select组件的触发按钮元素 * @param optionValue - 要选择的选项值 * @returns 如果成功选择了选项返回true,否则返回false */ export async function selectRadixOption(selectButton: HTMLElement, optionValue: string): Promise { const hiddenSelect = findHiddenSelectElement(selectButton); if (hiddenSelect) { // 如果有隐藏的select,使用userEvent.selectOptions await userEvent.selectOptions(hiddenSelect, optionValue); return true; } return false; } /** * 通过test ID查找并选择Radix UI Select组件的选项 * 这个函数封装了完整的查找、点击、选择流程 * * @param testId - Select组件的test ID * @param optionValue - 要选择的选项值 * @param user - userEvent实例(可选) * @returns 如果成功选择了选项返回true,否则返回false */ export async function selectRadixOptionByTestId( testId: string, optionValue: string, user = userEvent ): Promise { // 获取Select组件的触发按钮 const selectButton = screen.getByTestId(testId); // 点击打开下拉菜单 await user.click(selectButton); // 查找并选择选项 return await selectRadixOption(selectButton, optionValue); } /** * 等待Radix UI Select组件启用 * 有些Select组件在数据加载完成前是禁用的 * * @param testId - Select组件的test ID * @param timeout - 超时时间(毫秒),默认5000ms */ export async function waitForRadixSelectEnabled(testId: string, timeout = 5000): Promise { const startTime = Date.now(); while (Date.now() - startTime < timeout) { try { const selectElement = screen.getByTestId(testId); if (!selectElement.hasAttribute('disabled') && selectElement.getAttribute('aria-disabled') !== 'true') { return; } } catch (error) { // 元素可能还没渲染出来,继续等待 } // 等待一小段时间再检查 await new Promise(resolve => setTimeout(resolve, 100)); } throw new Error(`Radix Select with testId "${testId}" did not become enabled within ${timeout}ms`); } /** * 完整的Radix UI Select选择流程 * 包含等待启用、点击打开、选择选项的完整流程 * * @param testId - Select组件的test ID * @param optionValue - 要选择的选项值 * @param options - 配置选项 * @param options.useFireEvent - 是否使用fireEvent而不是userEvent.click(默认为false) * @param options.user - userEvent实例(可选) */ export async function completeRadixSelectFlow( testId: string, optionValue: string, options: { useFireEvent?: boolean; user?: typeof userEvent; } = {} ): Promise { const { useFireEvent = false, user = userEvent } = options; // 1. 等待Select组件启用 await waitForRadixSelectEnabled(testId); // 2. 获取Select按钮 const selectButton = screen.getByTestId(testId); // 3. 点击打开下拉菜单 if (useFireEvent) { // 有些Radix UI Select组件需要使用fireEvent而不是userEvent const { fireEvent } = await import('@testing-library/react'); fireEvent.click(selectButton); } else { await user.click(selectButton); } // 4. 查找并选择选项 const success = await selectRadixOption(selectButton, optionValue); if (!success) { // 如果通过隐藏select找不到,尝试直接点击选项文本 // 注意:这里假设选项文本已经在DOM中渲染 throw new Error(`Failed to select option "${optionValue}" for Radix Select with testId "${testId}". Make sure the dropdown is open and options are rendered.`); } }