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

基于React Testing Library生成PNG截图的问题求助(无需Puppeteer)

React Testing Library 生成PNG图片快照的无Puppeteer解决方案

问题分析

  1. 图片快照显示错误:你直接把HTML字符串传给toMatchImageSnapshot,但这个方法需要的是图片二进制数据(Buffer),而非HTML文本,导致生成的快照是无效PNG文件。
  2. html-to-image安装后测试报错:大概率是依赖版本不兼容、Jest未开启DOM环境支持,或是未正确处理异步操作导致。

解决方案步骤

1. 安装适配依赖

卸载可能冲突的旧版本,重新安装兼容版本:

npm uninstall html-to-image jest-image-snapshot
npm install --save-dev html-to-image@^1.11.11 jest-image-snapshot@^6.2.0

2. 调整Jest配置(按需)

在jest.config.js中确保启用DOM环境,并配置图片快照支持:

module.exports = {
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['./src/setupTests.js'],
  snapshotSerializers: ['jest-image-snapshot/serializer']
};

若有setupTests.js,可把expect.extend({ toMatchImageSnapshot });移到这里,避免每个测试文件重复编写。

3. 修正测试代码(核心)

重点修复图片快照生成逻辑,用html-to-image将渲染后的DOM转为PNG Buffer,再传入快照方法:

import { renderWithCtx as render } from 'js/testUtils';
import { act, cleanup, screen, waitFor } from '@testing-library/react';
import AwButton from 'viewmodel/AwButtonViewModel';
import { toMatchImageSnapshot } from 'jest-image-snapshot';
import { toPng } from 'html-to-image';

expect.extend({ toMatchImageSnapshot });

const mockProps = () => {
    return {
        id: 'id',
        'aria-label': 'btn',
        type: 'button',
        action: jest.fn(),
        tooltip: 'tooltip',
        iconId: 'cmdOpen',
        buttonType: 'accent-positive',
        size: 'auto'
    };
};

describe('testing AwButton component', function() {
    afterEach(cleanup);

    // 保留原有功能测试用例
    it('should display icon btn when specified iconId', async () => {
        let props = mockProps();
        let element = await render(<AwButton {...props}>mock btn</AwButton>);
        let icon = element.container.querySelector(`*[icon-id="${props.iconId}"]`);
        await waitFor(() => {
            expect(element).toBeTruthy();
            expect(icon).toBeTruthy();
            expect(screen.queryByText(/mock btn/i)).not.toBeTruthy();
        });
    });

    it('should display normal btn when not specified iconId', async () => {
        let props = mockProps();
        delete props.iconId;
        let element = await render(<AwButton {...props}>mock btn</AwButton>);
        let btn = element.container.querySelector('button');
        await waitFor(() => {
            expect(element).toBeTruthy();
            expect(screen.queryByText(/mock btn/i)).toBeTruthy();
            expect(btn.getAttribute('class')).toContain('accent-positive');
        });
    });

    it('should call click handler when btn was clicked', async () => {
        let props = mockProps();
        delete props.iconId;

        let element = await render(<AwButton {...props}>mock btn</AwButton>);
        let btn = element.container.querySelector('button');
        await act(async () => fireEvent.click(btn));
        await waitFor(() => {
            expect(element).toBeTruthy();
            expect(props.action).toHaveBeenCalled();
        });
    });

    // 修正后的图片快照测试用例
    it('should generate a valid screenshot of the button', async () => {
        const props = mockProps();
        const { container } = await render(<AwButton {...props}>mock btn</AwButton>);
        
        // 等待组件完全渲染(含样式、图标)
        await waitFor(() => screen.getByLabelText('btn'));

        // 将DOM元素转为PNG base64,再转成Buffer
        const pngBase64 = await toPng(container.firstChild, {
            backgroundColor: '#ffffff',
            scale: 2 // 提高分辨率避免模糊
        });
        const imageBuffer = Buffer.from(pngBase64.split(',')[1], 'base64');

        // 生成图片快照
        expect(imageBuffer).toMatchImageSnapshot({
            customSnapshotIdentifier: 'aw-button-icon-snapshot',
            failureThreshold: 0.01, // 允许1%的像素差异
            failureThresholdType: 'percent'
        });
    });
});

4. 解决测试命令报错

若执行node node_modules/@xyz/tooling/js/jest_wrapper.js test --watchAll=false仍报错:

  • 检查@xyz/tooling是否兼容当前Jest、html-to-image版本,尝试更新该工具包
  • 确保已安装jsdom:npm install --save-dev jsdom
  • 若样式缺失导致异常,在setupTests.js中注入全局样式:
    import '@your-project-path/styles/global.css';
    

关键注意点

  • 禁止直接传递HTML字符串给toMatchImageSnapshot,必须传入图片Buffer
  • 组件渲染、图片生成均为异步操作,需用await等待完成
  • jsdom默认不解析样式,需确保全局样式已注入,否则生成的图片可能无样式

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 21:36:07