You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Vitest测试SolidJS Portal组件生成的弹窗内容?

SolidJS Portal选择器弹窗的Vitest测试方案

问题背景

基于SolidJS的Portal组件实现了自定义选择器弹窗,目标是用Vitest覆盖弹窗内容的测试。最初直接渲染根组件后,触发弹窗打开按钮的点击事件,发现文档中并未生成预期的弹窗DIV(已确认点击事件触发成功)。尝试Mock Portal组件时,又遇到props.children始终为空的问题,无法测试由my_json_objects_collection生成的弹窗内容结构与数据。

高效测试方案

1. 直接定位Portal的实际挂载目标

SolidJS的Portal默认会把内容挂载到document.body,而非测试的默认容器里。测试时直接查询document.body即可找到弹窗内容,无需提前Mock:

import { render, screen, fireEvent } from '@testing-library/solid';

test('弹窗内容正确渲染', () => {
  const mockOptions = [{ label: '股票' }, { label: '债券' }];
  
  render(() => (
    <ComponentRoot options={mockOptions}>
      <PopperOpenerComponent />
      <Portal>
        {/* 由options生成的列表内容 */}
      </Portal>
    </ComponentRoot>
  ));

  // 触发弹窗打开
  fireEvent.click(screen.getByRole('button'));

  // 直接在body中查询弹窗内容
  expect(screen.getByText('股票')).toBeInTheDocument();
  expect(screen.getAllByRole('listitem')).toHaveLength(2);
});

2. 修复Portal的Mock逻辑

如果必须Mock Portal(比如避免真实DOM挂载),需要正确处理SolidJS的Children信号,不要手动解析子元素,直接把原组件生成的子内容渲染到测试容器中:

import { vi } from 'vitest';
import type { ParentProps } from 'solid-js';

vi.mock('solid-js/web', async () => {
  const actual = await vi.importActual('solid-js/web');
  return {
    ...actual,
    // 把Portal内容直接渲染到带标识的容器中,方便查询
    Portal: (props: ParentProps) => (
      <div data-testid="portal-container">{props.children}</div>
    )
  };
});

// 测试用例
test('Mock Portal后内容正确', () => {
  const mockOptions = [{ label: '黄金' }, { label: '原油' }];
  
  render(() => (
    <ComponentRoot options={mockOptions}>
      <PopperOpenerComponent />
      <Portal>
        {/* 由options生成的列表内容 */}
      </Portal>
    </ComponentRoot>
  ));

  fireEvent.click(screen.getByRole('button'));
  
  const portal = screen.getByTestId('portal-container');
  expect(portal).toHaveTextContent('黄金');
  expect(portal.querySelectorAll('li')).toHaveLength(2);
});

3. 单独测试内容生成逻辑

如果弹窗内容是由独立的渲染函数生成的,可以抽离该函数单独测试,无需依赖Portal和弹窗组件:

// 假设内容生成函数是generateOptionsList
import { generateOptionsList } from './your-component-file';

test('内容生成函数输出正确结构', () => {
  const mockOptions = [{ label: '基金', value: 'fund' }, { label: '衍生品', value: 'deriv' }];
  
  // 渲染生成的列表
  const { container } = render(() => generateOptionsList(mockOptions));
  
  expect(container.querySelectorAll('li')).toHaveLength(2);
  expect(container.querySelector('li[data-value="fund"]')?.textContent).toBe('基金');
});

内容的提问来源于stack exchange,提问作者Foxlab

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 14:10:23