{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "unit-testing-docs",
  "title": "Unit Testing Plate",
  "description": "Learn how to unit test Plate editor and plugins.",
  "files": [
    {
      "path": "../../content/docs/(guides)/unit-testing.mdx",
      "content": "---\ntitle: Unit Testing Plate\ndescription: Learn how to unit test Plate editor and plugins.\n---\n\nThis guide outlines best practices for unit testing Plate plugins and components using `@platejs/test-utils`.\n\n## Installation\n\n```bash\nnpm install @platejs/test-utils\n```\n\n## Setting Up Tests\n\nAdd the JSX pragma at the top of your test file:\n\n```typescript\n/** @jsx jsx */\n\nimport { jsx } from '@platejs/test-utils';\n\njsx; // so Biome doesn't remove unused imports\n```\n\nThis allows you to use JSX syntax for creating editor values.\n\n## Creating Test Cases\n\n### Editor State Representation\n\nUse JSX to represent editor states:\n\n```typescript\nconst input = (\n  <editor>\n    <hp>\n      Hello<cursor /> world\n    </hp>\n  </editor>\n) as any as PlateEditor;\n```\n\nNode elements like `<hp />`, `<hul />`, `<hli />` represent different types of nodes.\n\nSpecial elements like `<cursor />`, `<anchor />`, and `<focus />` represent selection states.\n\n### Testing Transforms\n\n1. Create an input state\n2. Define the expected output state\n3. Use `createPlateEditor` to set up the editor\n4. Apply the transform(s) directly\n5. Assert the editor's new state\n\nExample testing bold formatting:\n\n```typescript\nit('should apply bold formatting', () => {\n  const input = (\n    <editor>\n      <hp>\n        Hello <anchor />\n        world\n        <focus />\n      </hp>\n    </editor>\n  ) as any as PlateEditor;\n\n  const output = (\n    <editor>\n      <hp>\n        Hello <htext bold>world</htext>\n      </hp>\n    </editor>\n  ) as any as PlateEditor;\n\n  const editor = createPlateEditor({\n    plugins: [BoldPlugin],\n    value: input.children,\n    selection: input.selection,\n  });\n\n  // Apply transform directly\n  editor.tf.toggleMark('bold');\n\n  expect(editor.children).toEqual(output.children);\n});\n```\n\n### Testing Selection\n\nTest how operations affect the editor's selection:\n\n```typescript\nit('should collapse selection on backspace', () => {\n  const input = (\n    <editor>\n      <hp>\n        He<anchor />llo wor<focus />ld\n      </hp>\n    </editor>\n  ) as any as PlateEditor;\n\n  const output = (\n    <editor>\n      <hp>\n        He<cursor />ld\n      </hp>\n    </editor>\n  ) as any as PlateEditor;\n\n  const editor = createPlateEditor({\n    value: input.children,\n    selection: input.selection,\n  });\n\n  editor.tf.deleteBackward();\n\n  expect(editor.children).toEqual(output.children);\n  expect(editor.selection).toEqual(output.selection);\n});\n```\n\n## Testing Key Events\n\nWhen you need to test keyboard handlers directly:\n\n```typescript\nit('should call the onKeyDown handler', () => {\n  const input = (\n    <editor>\n      <hp>\n        Hello <anchor />world<focus />\n      </hp>\n    </editor>\n  ) as any as PlateEditor;\n\n  // Create a mock handler to verify it's called\n  const onKeyDownMock = jest.fn();\n\n  const editor = createPlateEditor({\n    value: input.children,\n    selection: input.selection,\n    plugins: [\n      {\n        key: 'test',\n        handlers: {\n          onKeyDown: onKeyDownMock,\n        },\n      },\n    ],\n  });\n\n  // Create the keyboard event\n  const event = new KeyboardEvent('keydown', {\n    key: 'Enter',\n  }) as any;\n\n  // Call the handler directly\n  editor.plugins.test.handlers.onKeyDown({\n    ...getEditorPlugin(editor, { key: 'test' }),\n    event,\n  });\n\n  // Verify the handler was called\n  expect(onKeyDownMock).toHaveBeenCalled();\n});\n```\n\n## Testing Complex Scenarios\n\nFor complex plugins like tables, test various scenarios by directly applying transforms:\n\n```typescript\ndescribe('Table plugin', () => {\n  it('should insert a table', () => {\n    const input = (\n      <editor>\n        <hp>\n          Test<cursor />\n        </hp>\n      </editor>\n    ) as any as PlateEditor;\n\n    const output = (\n      <editor>\n        <hp>Test</hp>\n        <htable>\n          <htr>\n            <htd>\n              <hp>\n                <cursor />\n              </hp>\n            </htd>\n            <htd>\n              <hp></hp>\n            </htd>\n          </htr>\n          <htr>\n            <htd>\n              <hp></hp>\n            </htd>\n            <htd>\n              <hp></hp>\n            </htd>\n          </htr>\n        </htable>\n      </editor>\n    ) as any as PlateEditor;\n\n    const editor = createPlateEditor({\n      value: input.children,\n      selection: input.selection,\n      plugins: [TablePlugin],\n    });\n\n    // Call transform directly\n    editor.tf.insertTable({ rows: 2, columns: 2 });\n\n    expect(editor.children).toEqual(output.children);\n    expect(editor.selection).toEqual(output.selection);\n  });\n});\n```\n\n## Testing Plugins with Options\n\nTest how different plugin options affect behavior:\n\n```typescript\ndescribe('when keepSelectedTextOnPaste is disabled', () => {\n  it('replaces the selected text with the pasted url', () => {\n    const input = (\n      <fragment>\n        <hp>\n          start <anchor />\n          of regular text\n          <focus />\n        </hp>\n      </fragment>\n    ) as any;\n\n    const output = (\n      <fragment>\n        <hp>\n          start <ha url=\"https://google.com\">https://google.com</ha>\n          <htext />\n        </hp>\n      </fragment>\n    ) as any;\n\n    const editor = createPlateEditor({\n      plugins: [\n        LinkPlugin.configure({\n          options: {\n            keepSelectedTextOnPaste: false,\n          },\n        }),\n      ],\n      value: input,\n    });\n\n    editor.tf.insertData({\n      getData: (type: string) => (type === 'text/plain' ? 'https://google.com' : ''),\n    } as any);\n\n    expect(input.children).toEqual(output.children);\n  });\n});\n```\n\n## Mocking vs. Real Transforms\n\nWhile mocking can be useful for isolating specific behaviors, Plate tests often assess actual editor children and selection after transforms. This approach ensures that plugins work correctly with the entire editor state.\n",
      "type": "registry:file",
      "target": "content/docs/plate/(guides)/unit-testing.mdx"
    }
  ],
  "type": "registry:file"
}