/** @jsx jsx */
import { jsx } from '@platejs/test-utils';
jsx; // so Biome doesn't remove unused importsThis allows you to use JSX syntax for creating editor values.
Use JSX to represent editor states:
const input = (
<editor>
<hp>
Hello<cursor /> world
</hp>
</editor>
) as any as PlateEditor;const input = (
<editor>
<hp>
Hello<cursor /> world
</hp>
</editor>
) as any as PlateEditor;Node elements like <hp />, <hul />, <hli /> represent different types of nodes.
Special elements like <cursor />, <anchor />, and <focus /> represent selection states.
createPlateEditor to set up the editorExample testing bold formatting:
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],
initialValue: input.children,
selection: input.selection,
});
editor.update((tx) => {
tx.marks.toggle('bold');
});
expect(editor.children).toEqual(output.children);
});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>
Test how operations affect the editor's selection:
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({
initialValue: input.children,
selection: input.selection,
});
editor.update((tx) => {
tx.text.deleteBackward({ unit: 'character' });
});
expect(editor.children).toEqual(output.children);
expect(editor.selection).toEqual(output.selection);
});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>
When you need to test keyboard handlers directly:
import { createPlateEditor, definePlatePlugin } from 'platejs/react';
it('should call the keyDown 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 keyDownMock = jest.fn();
const TestPlugin = definePlatePlugin('test', {
on: {
keyDown: keyDownMock,
},
});
const editor = createPlateEditor({
initialValue: input.children,
selection: input.selection,
plugins: [TestPlugin],
});
// Create the keyboard event
const event = new KeyboardEvent('keydown', {
key: 'Enter',
}) as any;
// Resolve the installed descriptor through its typed portal
const testPlugin = editor.plugin(TestPlugin);
testPlugin.on.keyDown?.({
...testPlugin,
event,
});
// Verify the handler was called
expect(keyDownMock).toHaveBeenCalled();
});import { createPlateEditor, definePlatePlugin } from 'platejs/react';
it('should call the keyDown 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 keyDownMock = jest.fn();
const TestPlugin = definePlatePlugin('test', {
For complex plugins like tables, test various scenarios by directly applying transforms:
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({
initialValue: input.children,
selection: input.selection,
plugins: [TablePlugin],
});
editor.update((tx) => {
tx.insert.table({ colCount: 2, rowCount: 2 });
});
expect(editor.children).toEqual(output.children);
expect(editor.selection).toEqual(output.selection);
});
});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>
Test how different initial state affects behavior:
describe('when keepSelectedTextOnPaste is disabled', () => {
it('replaces the selected text with the pasted url', () => {
const input = (
<fragment>
<hp>
start <anchor />
of regular text
<focus />
</hp>
</fragment>
) as any;
const output = (
<fragment>
<hp>
start <ha url="https://google.com">https://google.com</ha>
<htext />
</hp>
</fragment>
) as any;
const editor = createPlateEditor({
plugins: [
LinkPlugin.configure({
initialState: {
keepSelectedTextOnPaste: false,
},
}),
],
initialValue: input,
});
editor.api.dom.clipboard.insertData({
getData: (type: string) => (type === 'text/plain' ? 'https://google.com' : ''),
} as any);
expect(input.children).toEqual(output.children);
});
});describe('when keepSelectedTextOnPaste is disabled', () => {
it('replaces the selected text with the pasted url', () => {
const input = (
<fragment>
<hp>
start <anchor />
of regular text
<focus />
</hp>
</fragment>
) as any;
const output = (
<fragment>
<hp>
start <
While 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.