Back to Plate

Plate 单元测试

docs/(guides)/unit-testing.cn.mdx

1.0.05.7 KB
Original Source

本指南概述了使用 @platejs/test-utils 对 Plate 插件和组件进行单元测试的最佳实践。

安装

bash
npm install @platejs/test-utils

设置测试

在测试文件顶部添加 JSX pragma:

typescript
/** @jsx jsx */

import { jsx } from '@platejs/test-utils';

jsx; // so Biome doesn't remove unused imports

这允许你使用 JSX 语法创建编辑器值。

创建测试用例

编辑器状态表示

使用 JSX 表示编辑器状态:

typescript
const input = (
  <editor>
    <hp>
      Hello<cursor /> world
    </hp>
  </editor>
) as any as PlateEditor;

节点元素如 <hp /><hul /><hli /> 表示不同类型的节点。

特殊元素如 <cursor /><anchor /><focus /> 表示选区状态。

测试转换

  1. 创建输入状态
  2. 定义预期输出状态
  3. 使用 createPlateEditor 设置编辑器
  4. 直接应用转换
  5. 断言编辑器的新状态

测试粗体格式的示例:

typescript
it('should apply bold formatting', () => {
  const input = (
    <editor>
      <hp>
        Hello <anchor />
        world
        <focus />
      </hp>
    </editor>
  ) as any as PlateEditor;

  const output = (
    <editor>
      <hp>
        Hello <htext bold>world</htext>
      </hp>
    </editor>
  ) as any as PlateEditor;

  const editor = createPlateEditor({
    plugins: [BoldPlugin],
    value: input.children,
    selection: input.selection,
  });

  // Apply transform directly
  editor.tf.toggleMark('bold');

  expect(editor.children).toEqual(output.children);
});

测试选区

测试操作如何影响编辑器的选区:

typescript
it('should collapse selection on backspace', () => {
  const input = (
    <editor>
      <hp>
        He<anchor />llo wor<focus />ld
      </hp>
    </editor>
  ) as any as PlateEditor;

  const output = (
    <editor>
      <hp>
        He<cursor />ld
      </hp>
    </editor>
  ) as any as PlateEditor;

  const editor = createPlateEditor({
    value: input.children,
    selection: input.selection,
  });

  editor.tf.deleteBackward();

  expect(editor.children).toEqual(output.children);
  expect(editor.selection).toEqual(output.selection);
});

测试键盘事件

当你需要直接测试键盘处理程序时:

typescript
it('should call the onKeyDown handler', () => {
  const input = (
    <editor>
      <hp>
        Hello <anchor />world<focus />
      </hp>
    </editor>
  ) as any as PlateEditor;

  // Create a mock handler to verify it's called
  const onKeyDownMock = jest.fn();

  const editor = createPlateEditor({
    value: input.children,
    selection: input.selection,
    plugins: [
      {
        key: 'test',
        handlers: {
          onKeyDown: onKeyDownMock,
        },
      },
    ],
  });

  // Create the keyboard event
  const event = new KeyboardEvent('keydown', {
    key: 'Enter',
  }) as any;

  // Call the handler directly
  editor.plugins.test.handlers.onKeyDown({
    ...getEditorPlugin(editor, { key: 'test' }),
    event,
  });

  // Verify the handler was called
  expect(onKeyDownMock).toHaveBeenCalled();
});

测试复杂场景

对于像表格这样的复杂插件,通过直接应用转换来测试各种场景:

typescript
describe('Table plugin', () => {
  it('should insert a table', () => {
    const input = (
      <editor>
        <hp>
          Test<cursor />
        </hp>
      </editor>
    ) as any as PlateEditor;

    const output = (
      <editor>
        <hp>Test</hp>
        <htable>
          <htr>
            <htd>
              <hp>
                <cursor />
              </hp>
            </htd>
            <htd>
              <hp></hp>
            </htd>
          </htr>
          <htr>
            <htd>
              <hp></hp>
            </htd>
            <htd>
              <hp></hp>
            </htd>
          </htr>
        </htable>
      </editor>
    ) as any as PlateEditor;

    const editor = createPlateEditor({
      value: input.children,
      selection: input.selection,
      plugins: [TablePlugin],
    });

    // Call transform directly
    editor.tf.insertTable({ rows: 2, columns: 2 });

    expect(editor.children).toEqual(output.children);
    expect(editor.selection).toEqual(output.selection);
  });
});

测试带选项的插件

测试不同插件选项如何影响行为:

typescript
describe('when undo is enabled', () => {
  it('should undo text format upon delete', () => {
    const input = (
      <fragment>
        <hp>
          1/<cursor />
        </hp>
      </fragment>
    ) as any;

    const output = (
      <fragment>
        <hp>
          1/4<cursor />
        </hp>
      </fragment>
    ) as any;

    const editor = createPlateEditor({
      plugins: [
        AutoformatPlugin.configure({
          options: {
            enableUndoOnDelete: true,
            rules: [
              {
                format: '¼',
                match: '1/4',
                mode: 'text',
              },
            ],
          },
        }),
      ],
      value: input,
    });

    // Trigger the autoformat
    editor.tf.insertText('4');

    // Simulate backspace key
    const event = new KeyboardEvent('keydown', {
      key: 'backspace',
    }) as any;

    // Call the handler
    editor.getPlugin({key: KEYS.autoformat}).handlers.onKeyDown({
      ...getEditorPlugin(editor, AutoformatPlugin),
      event,
    });

    // With enableUndoOnDelete: true, pressing backspace should restore the original text
    expect(input.children).toEqual(output.children);
  });
});

Mock 与真实转换

虽然 mock 对于隔离特定行为很有用,但 Plate 测试通常会在转换后评估实际的编辑器 children 和选区。这种方法确保插件能与整个编辑器状态正确协作。