Skip to content

Testing your editor

prosekit/core/test provides a small layer for unit-testing extensions and editors without spinning up a browser.

createTestEditor(options) is a drop-in alternative to createEditor that returns a TestEditor. It exposes the same API as Editor, plus helpers for setting the document and selection from a builder syntax.

import { unionfunction union<const E extends readonly Extension[]>(...exts: E): Union<E> (+1 overload)
Merges multiple extensions into one. You can pass multiple extensions as arguments or a single array containing multiple extensions.
@throwsIf no extensions are provided.@example```ts function defineFancyNodes() { return union( defineFancyParagraph(), defineFancyHeading(), ) } ```@example```ts function defineFancyNodes() { return union([ defineFancyParagraph(), defineFancyHeading(), ]) } ```
} from 'prosekit/core'
import { createTestEditorfunction createTestEditor<E extends Extension>(options: EditorOptions<E>): TestEditor<E> } from 'prosekit/core/test' import { defineDocfunction defineDoc(): DocExtension } from 'prosekit/extensions/doc' import { defineParagraphfunction defineParagraph(): ParagraphExtension
Defines a paragraph node. The paragraph node spec has the highest priority, because it should be the default block node for most cases.
} from 'prosekit/extensions/paragraph'
import { defineTextfunction defineText(): TextExtension } from 'prosekit/extensions/text' const editorconst editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>> = createTestEditorcreateTestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>(options: EditorOptions<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>): TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>({ extensionEditorOptions<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>.extension: Union<readonly [DocExtension, TextExtension, ParagraphExtension]>
The extension to use when creating the editor.
: unionunion<readonly [DocExtension, TextExtension, ParagraphExtension]>(exts_0: DocExtension, exts_1: TextExtension, exts_2: ParagraphExtension): Union<readonly [DocExtension, TextExtension, ParagraphExtension]> (+1 overload)
Merges multiple extensions into one. You can pass multiple extensions as arguments or a single array containing multiple extensions.
@throwsIf no extensions are provided.@example```ts function defineFancyNodes() { return union( defineFancyParagraph(), defineFancyHeading(), ) } ```@example```ts function defineFancyNodes() { return union([ defineFancyParagraph(), defineFancyHeading(), ]) } ```
(defineDocfunction defineDoc(): DocExtension(), defineTextfunction defineText(): TextExtension(), defineParagraphfunction defineParagraph(): ParagraphExtension
Defines a paragraph node. The paragraph node spec has the highest priority, because it should be the default block node for most cases.
()),
})

You don't call editor.mount(...). The test editor manages a detached view internally.

editor.nodes.<name>(...) and editor.marks.<name>(...) are typed factories generated from your schema. Use them to build a document and hand it to editor.set(...).

import { unionfunction union<const E extends readonly Extension[]>(...exts: E): Union<E> (+1 overload)
Merges multiple extensions into one. You can pass multiple extensions as arguments or a single array containing multiple extensions.
@throwsIf no extensions are provided.@example```ts function defineFancyNodes() { return union( defineFancyParagraph(), defineFancyHeading(), ) } ```@example```ts function defineFancyNodes() { return union([ defineFancyParagraph(), defineFancyHeading(), ]) } ```
} from 'prosekit/core'
import { createTestEditorfunction createTestEditor<E extends Extension>(options: EditorOptions<E>): TestEditor<E> } from 'prosekit/core/test' import { defineBoldfunction defineBold(): BoldExtension } from 'prosekit/extensions/bold' import { defineDocfunction defineDoc(): DocExtension } from 'prosekit/extensions/doc' import { defineParagraphfunction defineParagraph(): ParagraphExtension
Defines a paragraph node. The paragraph node spec has the highest priority, because it should be the default block node for most cases.
} from 'prosekit/extensions/paragraph'
import { defineTextfunction defineText(): TextExtension } from 'prosekit/extensions/text' const editorconst editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>> = createTestEditorcreateTestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>(options: EditorOptions<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>): TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>({ extensionEditorOptions<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>.extension: Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>
The extension to use when creating the editor.
: unionunion<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>(exts_0: DocExtension, exts_1: TextExtension, exts_2: ParagraphExtension, exts_3: BoldExtension): Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]> (+1 overload)
Merges multiple extensions into one. You can pass multiple extensions as arguments or a single array containing multiple extensions.
@throwsIf no extensions are provided.@example```ts function defineFancyNodes() { return union( defineFancyParagraph(), defineFancyHeading(), ) } ```@example```ts function defineFancyNodes() { return union([ defineFancyParagraph(), defineFancyHeading(), ]) } ```
(
defineDocfunction defineDoc(): DocExtension(), defineTextfunction defineText(): TextExtension(), defineParagraphfunction defineParagraph(): ParagraphExtension
Defines a paragraph node. The paragraph node spec has the highest priority, because it should be the default block node for most cases.
(),
defineBoldfunction defineBold(): BoldExtension(), ), }) const n
const n: ToNodeAction<SimplifyDeeper<{
    doc: {
        readonly [x: string]: any;
    };
    paragraph: {
        readonly [x: string]: any;
    };
    text: {
        readonly [x: string]: any;
    };
}>>
= editorconst editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>.nodes
Editor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>.nodes: ToNodeAction<SimplifyDeeper<{
    doc: {
        readonly [x: string]: any;
    };
    paragraph: {
        readonly [x: string]: any;
    };
    text: {
        readonly [x: string]: any;
    };
}>>
All {@link NodeAction } s defined by the editor.
const m
const m: ToMarkAction<SimplifyDeeper<{
    bold: {
        readonly [x: string]: any;
    };
}>>
= editorconst editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>.marks
Editor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>.marks: ToMarkAction<SimplifyDeeper<{
    bold: {
        readonly [x: string]: any;
    };
}>>
All {@link MarkAction } s defined by the editor.
const docconst doc: Node = n
const n: ToNodeAction<SimplifyDeeper<{
    doc: {
        readonly [x: string]: any;
    };
    paragraph: {
        readonly [x: string]: any;
    };
    text: {
        readonly [x: string]: any;
    };
}>>
.doc
doc: NodeAction
(attrs: {
    readonly [x: string]: any;
} | null, ...children: NodeChild[]) => Node (+1 overload)
Creates a node with attributes and any number of children.
(
n
const n: ToNodeAction<SimplifyDeeper<{
    doc: {
        readonly [x: string]: any;
    };
    paragraph: {
        readonly [x: string]: any;
    };
    text: {
        readonly [x: string]: any;
    };
}>>
.paragraph
paragraph: NodeAction
(...children: NodeChild[]) => Node (+1 overload)
Creates a node with any number of children.
('Hello, ', m
const m: ToMarkAction<SimplifyDeeper<{
    bold: {
        readonly [x: string]: any;
    };
}>>
.bold
bold: MarkAction
(...children: NodeChild[]) => Node[] (+1 overload)
Applies a mark with any number of children.
('world'), '!'),
) editorconst editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>.setTestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>.set(doc: Node): void
Set the editor state to the given document. You can use special tokens `<a>` and `<b>` to set the anchor and head positions of the selection.
@example```ts const editor = createTestEditor({ extension }) const n = editor.nodes const doc = n.doc(n.paragraph('<a>Hello<b> world!')) editor.set(doc) // "Hello" is selected. ```
(docconst doc: Node)

When you only need to build a document and not run commands, you can skip the editor entirely. createNodeBuilders(schema) and createMarkBuilders(schema) return the same typed factories as editor.nodes / editor.marks, but they take a schema directly and omit isActive. Pass your extension type as the type argument to type them to your schema.

import { createMarkBuildersfunction createMarkBuilders<E extends Extension>(schema: Schema): ExtractMarkBuilders<E>
Creates a set of typed mark builders from a {@link Schema } . Each returned builder applies one mark type from the schema to its children and returns the resulting array of ProseMirror nodes. A builder accepts an optional attributes object followed by any number of children, where a child is a node, a string, or a nested array of children. You can use these builders without creating an editor, for example in tests or when rendering on the server. Pass your extension type as the type argument to type the builders to your schema's mark names and attributes.
@paramschema - The schema to create mark builders for.@example```ts import { createNodeBuilders, createMarkBuilders } from 'prosekit/core' import { defineBasicExtension } from 'prosekit/basic' const extension = defineBasicExtension() const schema = extension.schema! const n = createNodeBuilders<typeof extension>(schema) const m = createMarkBuilders<typeof extension>(schema) const paragraph = n.paragraph('Hello ', m.bold('world', m.italic('!'))) ```
, createNodeBuildersfunction createNodeBuilders<E extends Extension>(schema: Schema): ExtractNodeBuilders<E>
Creates a set of typed node builders from a {@link Schema } . Each returned builder creates a ProseMirror node for one node type in the schema. A builder accepts an optional attributes object followed by any number of children, where a child is a node, a string, or a nested array of children. You can use these builders without creating an editor, for example in tests or when rendering on the server. Pass your extension type as the type argument to type the builders to your schema's node names and attributes.
@paramschema - The schema to create node builders for.@example```ts import { createNodeBuilders } from 'prosekit/core' import { defineBasicExtension } from 'prosekit/basic' const extension = defineBasicExtension() const schema = extension.schema! const n = createNodeBuilders<typeof extension>(schema) const paragraph = n.paragraph('Hello world') const heading = n.heading({ level: 1 }, 'Title') const doc = n.doc(heading, paragraph) ```
, unionfunction union<const E extends readonly Extension[]>(...exts: E): Union<E> (+1 overload)
Merges multiple extensions into one. You can pass multiple extensions as arguments or a single array containing multiple extensions.
@throwsIf no extensions are provided.@example```ts function defineFancyNodes() { return union( defineFancyParagraph(), defineFancyHeading(), ) } ```@example```ts function defineFancyNodes() { return union([ defineFancyParagraph(), defineFancyHeading(), ]) } ```
} from 'prosekit/core'
import { defineBoldfunction defineBold(): BoldExtension } from 'prosekit/extensions/bold' import { defineDocfunction defineDoc(): DocExtension } from 'prosekit/extensions/doc' import { defineHeadingfunction defineHeading(): HeadingExtension } from 'prosekit/extensions/heading' import { defineParagraphfunction defineParagraph(): ParagraphExtension
Defines a paragraph node. The paragraph node spec has the highest priority, because it should be the default block node for most cases.
} from 'prosekit/extensions/paragraph'
import { defineTextfunction defineText(): TextExtension } from 'prosekit/extensions/text' function defineTestExtensionfunction defineTestExtension(): Union<readonly [DocExtension, TextExtension, ParagraphExtension, HeadingExtension, BoldExtension]>() { return unionunion<readonly [DocExtension, TextExtension, ParagraphExtension, HeadingExtension, BoldExtension]>(exts_0: DocExtension, exts_1: TextExtension, exts_2: ParagraphExtension, exts_3: HeadingExtension, exts_4: BoldExtension): Union<readonly [DocExtension, TextExtension, ParagraphExtension, HeadingExtension, BoldExtension]> (+1 overload)
Merges multiple extensions into one. You can pass multiple extensions as arguments or a single array containing multiple extensions.
@throwsIf no extensions are provided.@example```ts function defineFancyNodes() { return union( defineFancyParagraph(), defineFancyHeading(), ) } ```@example```ts function defineFancyNodes() { return union([ defineFancyParagraph(), defineFancyHeading(), ]) } ```
(
defineDocfunction defineDoc(): DocExtension(), defineTextfunction defineText(): TextExtension(), defineParagraphfunction defineParagraph(): ParagraphExtension
Defines a paragraph node. The paragraph node spec has the highest priority, because it should be the default block node for most cases.
(),
defineHeadingfunction defineHeading(): HeadingExtension(), defineBoldfunction defineBold(): BoldExtension(), ) } type TestExtension
type TestExtension = Extension<{
    Nodes: SimplifyDeeper<{
        doc: Attrs;
        heading: {
            level: number;
        };
        paragraph: {
            readonly [x: string]: any;
        };
        text: Attrs;
    }>;
    Marks: SimplifyDeeper<{
        bold: {
            readonly [x: string]: any;
        };
    }>;
    Commands: {
        toggleBold: [];
        setHeading: [attrs?: HeadingAttrs | undefined];
        insertHeading: [attrs?: HeadingAttrs | undefined];
        toggleHeading: [attrs?: HeadingAttrs | undefined];
        setParagraph: [];
    };
}>
= ReturnTypetype ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any
Obtain the return type of a function type
<typeof defineTestExtensionfunction defineTestExtension(): Union<readonly [DocExtension, TextExtension, ParagraphExtension, HeadingExtension, BoldExtension]>>
const extensionconst extension: Union<readonly [DocExtension, TextExtension, ParagraphExtension, HeadingExtension, BoldExtension]>: TestExtension
type TestExtension = Extension<{
    Nodes: SimplifyDeeper<{
        doc: Attrs;
        heading: {
            level: number;
        };
        paragraph: {
            readonly [x: string]: any;
        };
        text: Attrs;
    }>;
    Marks: SimplifyDeeper<{
        bold: {
            readonly [x: string]: any;
        };
    }>;
    Commands: {
        toggleBold: [];
        setHeading: [attrs?: HeadingAttrs | undefined];
        insertHeading: [attrs?: HeadingAttrs | undefined];
        toggleHeading: [attrs?: HeadingAttrs | undefined];
        setParagraph: [];
    };
}>
= defineTestExtensionfunction defineTestExtension(): Union<readonly [DocExtension, TextExtension, ParagraphExtension, HeadingExtension, BoldExtension]>()
const schemaconst schema: Schema<any, any> = extensionconst extension: Union<readonly [DocExtension, TextExtension, ParagraphExtension, HeadingExtension, BoldExtension]>.schemaExtension<T extends ExtensionTyping<any, any, any> = ExtensionTyping<any, any, any>>.schema: Schema<any, any> | null
The schema that this extension represents.
!
const n
const n: ToNodeBuilder<SimplifyDeeper<{
    doc: {
        readonly [x: string]: any;
    };
    heading: {
        level: number;
    };
    paragraph: {
        readonly [x: string]: any;
    };
    text: {
        readonly [x: string]: any;
    };
}>>
= createNodeBuilders
createNodeBuilders<Union<readonly [DocExtension, TextExtension, ParagraphExtension, HeadingExtension, BoldExtension]>>(schema: Schema): ToNodeBuilder<SimplifyDeeper<{
    doc: {
        readonly [x: string]: any;
    };
    heading: {
        level: number;
    };
    paragraph: {
        readonly [x: string]: any;
    };
    text: {
        readonly [x: string]: any;
    };
}>>
Creates a set of typed node builders from a {@link Schema } . Each returned builder creates a ProseMirror node for one node type in the schema. A builder accepts an optional attributes object followed by any number of children, where a child is a node, a string, or a nested array of children. You can use these builders without creating an editor, for example in tests or when rendering on the server. Pass your extension type as the type argument to type the builders to your schema's node names and attributes.
@paramschema - The schema to create node builders for.@example```ts import { createNodeBuilders } from 'prosekit/core' import { defineBasicExtension } from 'prosekit/basic' const extension = defineBasicExtension() const schema = extension.schema! const n = createNodeBuilders<typeof extension>(schema) const paragraph = n.paragraph('Hello world') const heading = n.heading({ level: 1 }, 'Title') const doc = n.doc(heading, paragraph) ```
<TestExtension
type TestExtension = Extension<{
    Nodes: SimplifyDeeper<{
        doc: Attrs;
        heading: {
            level: number;
        };
        paragraph: {
            readonly [x: string]: any;
        };
        text: Attrs;
    }>;
    Marks: SimplifyDeeper<{
        bold: {
            readonly [x: string]: any;
        };
    }>;
    Commands: {
        toggleBold: [];
        setHeading: [attrs?: HeadingAttrs | undefined];
        insertHeading: [attrs?: HeadingAttrs | undefined];
        toggleHeading: [attrs?: HeadingAttrs | undefined];
        setParagraph: [];
    };
}>
>(schemaconst schema: Schema<any, any>)
const m
const m: ToMarkBuilder<SimplifyDeeper<{
    bold: {
        readonly [x: string]: any;
    };
}>>
= createMarkBuilders
createMarkBuilders<Union<readonly [DocExtension, TextExtension, ParagraphExtension, HeadingExtension, BoldExtension]>>(schema: Schema): ToMarkBuilder<SimplifyDeeper<{
    bold: {
        readonly [x: string]: any;
    };
}>>
Creates a set of typed mark builders from a {@link Schema } . Each returned builder applies one mark type from the schema to its children and returns the resulting array of ProseMirror nodes. A builder accepts an optional attributes object followed by any number of children, where a child is a node, a string, or a nested array of children. You can use these builders without creating an editor, for example in tests or when rendering on the server. Pass your extension type as the type argument to type the builders to your schema's mark names and attributes.
@paramschema - The schema to create mark builders for.@example```ts import { createNodeBuilders, createMarkBuilders } from 'prosekit/core' import { defineBasicExtension } from 'prosekit/basic' const extension = defineBasicExtension() const schema = extension.schema! const n = createNodeBuilders<typeof extension>(schema) const m = createMarkBuilders<typeof extension>(schema) const paragraph = n.paragraph('Hello ', m.bold('world', m.italic('!'))) ```
<TestExtension
type TestExtension = Extension<{
    Nodes: SimplifyDeeper<{
        doc: Attrs;
        heading: {
            level: number;
        };
        paragraph: {
            readonly [x: string]: any;
        };
        text: Attrs;
    }>;
    Marks: SimplifyDeeper<{
        bold: {
            readonly [x: string]: any;
        };
    }>;
    Commands: {
        toggleBold: [];
        setHeading: [attrs?: HeadingAttrs | undefined];
        insertHeading: [attrs?: HeadingAttrs | undefined];
        toggleHeading: [attrs?: HeadingAttrs | undefined];
        setParagraph: [];
    };
}>
>(schemaconst schema: Schema<any, any>)
const docconst doc: Node = n
const n: ToNodeBuilder<SimplifyDeeper<{
    doc: {
        readonly [x: string]: any;
    };
    heading: {
        level: number;
    };
    paragraph: {
        readonly [x: string]: any;
    };
    text: {
        readonly [x: string]: any;
    };
}>>
.doc
doc: NodeBuilder
(attrs: {
    readonly [x: string]: any;
} | null, ...children: NodeChild[]) => Node (+1 overload)
Creates a node with attributes and any number of children.
(
n
const n: ToNodeBuilder<SimplifyDeeper<{
    doc: {
        readonly [x: string]: any;
    };
    heading: {
        level: number;
    };
    paragraph: {
        readonly [x: string]: any;
    };
    text: {
        readonly [x: string]: any;
    };
}>>
.heading
heading: NodeBuilder
(attrs: {
    level: number;
} | null, ...children: NodeChild[]) => Node (+1 overload)
Creates a node with attributes and any number of children.
({ levellevel: number: 1 }, 'Title'),
n
const n: ToNodeBuilder<SimplifyDeeper<{
    doc: {
        readonly [x: string]: any;
    };
    heading: {
        level: number;
    };
    paragraph: {
        readonly [x: string]: any;
    };
    text: {
        readonly [x: string]: any;
    };
}>>
.paragraph
paragraph: NodeBuilder
(...children: NodeChild[]) => Node (+1 overload)
Creates a node with any number of children.
('Hello, ', m
const m: ToMarkBuilder<SimplifyDeeper<{
    bold: {
        readonly [x: string]: any;
    };
}>>
.bold
bold: MarkBuilder
(...children: NodeChild[]) => Node[] (+1 overload)
Applies a mark with any number of children.
('world'), '!'),
)

The set helper recognizes two special tokens in text content:

  • <a> marks the start of the selection.
  • <b> marks the end of the selection.
import { unionfunction union<const E extends readonly Extension[]>(...exts: E): Union<E> (+1 overload)
Merges multiple extensions into one. You can pass multiple extensions as arguments or a single array containing multiple extensions.
@throwsIf no extensions are provided.@example```ts function defineFancyNodes() { return union( defineFancyParagraph(), defineFancyHeading(), ) } ```@example```ts function defineFancyNodes() { return union([ defineFancyParagraph(), defineFancyHeading(), ]) } ```
} from 'prosekit/core'
import { createTestEditorfunction createTestEditor<E extends Extension>(options: EditorOptions<E>): TestEditor<E> } from 'prosekit/core/test' import { defineDocfunction defineDoc(): DocExtension } from 'prosekit/extensions/doc' import { defineParagraphfunction defineParagraph(): ParagraphExtension
Defines a paragraph node. The paragraph node spec has the highest priority, because it should be the default block node for most cases.
} from 'prosekit/extensions/paragraph'
import { defineTextfunction defineText(): TextExtension } from 'prosekit/extensions/text' const editorconst editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>> = createTestEditorcreateTestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>(options: EditorOptions<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>): TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>({ extensionEditorOptions<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>.extension: Union<readonly [DocExtension, TextExtension, ParagraphExtension]>
The extension to use when creating the editor.
: unionunion<readonly [DocExtension, TextExtension, ParagraphExtension]>(exts_0: DocExtension, exts_1: TextExtension, exts_2: ParagraphExtension): Union<readonly [DocExtension, TextExtension, ParagraphExtension]> (+1 overload)
Merges multiple extensions into one. You can pass multiple extensions as arguments or a single array containing multiple extensions.
@throwsIf no extensions are provided.@example```ts function defineFancyNodes() { return union( defineFancyParagraph(), defineFancyHeading(), ) } ```@example```ts function defineFancyNodes() { return union([ defineFancyParagraph(), defineFancyHeading(), ]) } ```
(defineDocfunction defineDoc(): DocExtension(), defineTextfunction defineText(): TextExtension(), defineParagraphfunction defineParagraph(): ParagraphExtension
Defines a paragraph node. The paragraph node spec has the highest priority, because it should be the default block node for most cases.
()),
}) const n
const n: ToNodeAction<SimplifyDeeper<{
    doc: {
        readonly [x: string]: any;
    };
    paragraph: {
        readonly [x: string]: any;
    };
    text: {
        readonly [x: string]: any;
    };
}>>
= editorconst editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>.nodes
Editor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>.nodes: ToNodeAction<SimplifyDeeper<{
    doc: {
        readonly [x: string]: any;
    };
    paragraph: {
        readonly [x: string]: any;
    };
    text: {
        readonly [x: string]: any;
    };
}>>
All {@link NodeAction } s defined by the editor.
const docconst doc: Node = n
const n: ToNodeAction<SimplifyDeeper<{
    doc: {
        readonly [x: string]: any;
    };
    paragraph: {
        readonly [x: string]: any;
    };
    text: {
        readonly [x: string]: any;
    };
}>>
.doc
doc: NodeAction
(attrs: {
    readonly [x: string]: any;
} | null, ...children: NodeChild[]) => Node (+1 overload)
Creates a node with attributes and any number of children.
(n
const n: ToNodeAction<SimplifyDeeper<{
    doc: {
        readonly [x: string]: any;
    };
    paragraph: {
        readonly [x: string]: any;
    };
    text: {
        readonly [x: string]: any;
    };
}>>
.paragraph
paragraph: NodeAction
(...children: NodeChild[]) => Node (+1 overload)
Creates a node with any number of children.
('<a>Hello<b> world!'))
editorconst editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>.setTestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>.set(doc: Node): void
Set the editor state to the given document. You can use special tokens `<a>` and `<b>` to set the anchor and head positions of the selection.
@example```ts const editor = createTestEditor({ extension }) const n = editor.nodes const doc = n.doc(n.paragraph('<a>Hello<b> world!')) editor.set(doc) // "Hello" is selected. ```
(docconst doc: Node)
// "Hello" is now selected.

Reading a selection back with extractSelection

Section titled “Reading a selection back with extractSelection”

extractSelection(doc) reads the <a>/<b> tokens out of a tagged document and returns the matching Selection. Use it in tests to assert against an expected tagged document instead of hand-counting positions.

import { unionfunction union<const E extends readonly Extension[]>(...exts: E): Union<E> (+1 overload)
Merges multiple extensions into one. You can pass multiple extensions as arguments or a single array containing multiple extensions.
@throwsIf no extensions are provided.@example```ts function defineFancyNodes() { return union( defineFancyParagraph(), defineFancyHeading(), ) } ```@example```ts function defineFancyNodes() { return union([ defineFancyParagraph(), defineFancyHeading(), ]) } ```
} from 'prosekit/core'
import { createTestEditorfunction createTestEditor<E extends Extension>(options: EditorOptions<E>): TestEditor<E>, extractSelectionfunction extractSelection(doc: Node): Selection | undefined
Extracts a {@link Selection } from a tagged ProseMirror document built with the test node builders. The position of the `<a>` token becomes the anchor, and the optional `<b>` token becomes the head. Returns a `TextSelection` when `<a>` resolves inside inline content, a `NodeSelection` when `<a>` resolves inside a block parent, or `undefined` when the document contains no tags.
@exampleExtracting a `TextSelection`: ```ts const editor = createTestEditor({ extension }) const n = editor.nodes const doc = n.doc(n.paragraph('<a>Hello<b> world!')) const selection = extractSelection(doc) // TextSelection covering "Hello" ```@exampleExtracting a `NodeSelection`: ```ts const editor = createTestEditor({ extension }) const n = editor.nodes const doc = n.doc('<a>', n.paragraph('foo')) const selection = extractSelection(doc) // NodeSelection on the paragraph ```@exampleA document without tags returns `undefined`: ```ts const editor = createTestEditor({ extension }) const n = editor.nodes const doc = n.doc(n.paragraph('Hello world!')) const selection = extractSelection(doc) // undefined ```
} from 'prosekit/core/test'
import { defineDocfunction defineDoc(): DocExtension } from 'prosekit/extensions/doc' import { defineParagraphfunction defineParagraph(): ParagraphExtension
Defines a paragraph node. The paragraph node spec has the highest priority, because it should be the default block node for most cases.
} from 'prosekit/extensions/paragraph'
import { defineTextfunction defineText(): TextExtension } from 'prosekit/extensions/text' import { expectconst expect: ExpectStatic, itconst it: TestAPI
Defines a test case with a given name and test function. The test function can optionally be configured with test options.
@paramname - The name of the test or a function that will be used as a test name.@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.@throws{Error} If called inside another test function.@example```ts // Define a simple test it('adds two numbers', () => { expect(add(1, 2)).toBe(3); }); ```@example```ts // Define a test with options it('subtracts two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); }); ```
} from 'vitest'
const editorconst editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>> = createTestEditorcreateTestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>(options: EditorOptions<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>): TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>({ extensionEditorOptions<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>.extension: Union<readonly [DocExtension, TextExtension, ParagraphExtension]>
The extension to use when creating the editor.
: unionunion<readonly [DocExtension, TextExtension, ParagraphExtension]>(exts_0: DocExtension, exts_1: TextExtension, exts_2: ParagraphExtension): Union<readonly [DocExtension, TextExtension, ParagraphExtension]> (+1 overload)
Merges multiple extensions into one. You can pass multiple extensions as arguments or a single array containing multiple extensions.
@throwsIf no extensions are provided.@example```ts function defineFancyNodes() { return union( defineFancyParagraph(), defineFancyHeading(), ) } ```@example```ts function defineFancyNodes() { return union([ defineFancyParagraph(), defineFancyHeading(), ]) } ```
(defineDocfunction defineDoc(): DocExtension(), defineTextfunction defineText(): TextExtension(), defineParagraphfunction defineParagraph(): ParagraphExtension
Defines a paragraph node. The paragraph node spec has the highest priority, because it should be the default block node for most cases.
()),
}) const n
const n: ToNodeAction<SimplifyDeeper<{
    doc: {
        readonly [x: string]: any;
    };
    paragraph: {
        readonly [x: string]: any;
    };
    text: {
        readonly [x: string]: any;
    };
}>>
= editorconst editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>.nodes
Editor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>.nodes: ToNodeAction<SimplifyDeeper<{
    doc: {
        readonly [x: string]: any;
    };
    paragraph: {
        readonly [x: string]: any;
    };
    text: {
        readonly [x: string]: any;
    };
}>>
All {@link NodeAction } s defined by the editor.
itit<object>(name: string | Function, fn?: TestFunction<object> | undefined, options?: number): void (+1 overload)
Defines a test case with a given name and test function. The test function can optionally be configured with test options.
@paramname - The name of the test or a function that will be used as a test name.@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.@throws{Error} If called inside another test function.@example```ts // Define a simple test it('adds two numbers', () => { expect(add(1, 2)).toBe(3); }); ```@example```ts // Define a test with options it('subtracts two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); }); ```
('preserves the selection that <a> and <b> describe', () => {
const docconst doc: Node = n
const n: ToNodeAction<SimplifyDeeper<{
    doc: {
        readonly [x: string]: any;
    };
    paragraph: {
        readonly [x: string]: any;
    };
    text: {
        readonly [x: string]: any;
    };
}>>
.doc
doc: NodeAction
(attrs: {
    readonly [x: string]: any;
} | null, ...children: NodeChild[]) => Node (+1 overload)
Creates a node with attributes and any number of children.
(n
const n: ToNodeAction<SimplifyDeeper<{
    doc: {
        readonly [x: string]: any;
    };
    paragraph: {
        readonly [x: string]: any;
    };
    text: {
        readonly [x: string]: any;
    };
}>>
.paragraph
paragraph: NodeAction
(...children: NodeChild[]) => Node (+1 overload)
Creates a node with any number of children.
('<a>Hello<b> world!'))
editorconst editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>.setTestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>.set(doc: Node): void
Set the editor state to the given document. You can use special tokens `<a>` and `<b>` to set the anchor and head positions of the selection.
@example```ts const editor = createTestEditor({ extension }) const n = editor.nodes const doc = n.doc(n.paragraph('<a>Hello<b> world!')) editor.set(doc) // "Hello" is selected. ```
(docconst doc: Node)
expectexpect<any>(actual: any, message?: string): Assertion<any> (+1 overload)(editorconst editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>.stateEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>.state: EditorState
The editor's current state.
.selectionEditorState.selection: Selection
The selection.
.toJSONSelection.toJSON(): any
Convert the selection to a JSON representation. When implementing this for a custom selection class, make sure to give the object a `type` property whose value matches the ID under which you [registered](https://prosemirror.net/docs/ref/#state.Selection^jsonID) your class.
()).toEqualJestAssertion<any>.toEqual: <any>(expected: any) => void
Used when you want to check that two objects have the same value. This matcher recursively checks the equality of all fields, rather than checking for object identity.
@exampleexpect(user).toEqual({ name: 'Alice', age: 30 });
(
extractSelectionfunction extractSelection(doc: Node): Selection | undefined
Extracts a {@link Selection } from a tagged ProseMirror document built with the test node builders. The position of the `<a>` token becomes the anchor, and the optional `<b>` token becomes the head. Returns a `TextSelection` when `<a>` resolves inside inline content, a `NodeSelection` when `<a>` resolves inside a block parent, or `undefined` when the document contains no tags.
@exampleExtracting a `TextSelection`: ```ts const editor = createTestEditor({ extension }) const n = editor.nodes const doc = n.doc(n.paragraph('<a>Hello<b> world!')) const selection = extractSelection(doc) // TextSelection covering "Hello" ```@exampleExtracting a `NodeSelection`: ```ts const editor = createTestEditor({ extension }) const n = editor.nodes const doc = n.doc('<a>', n.paragraph('foo')) const selection = extractSelection(doc) // NodeSelection on the paragraph ```@exampleA document without tags returns `undefined`: ```ts const editor = createTestEditor({ extension }) const n = editor.nodes const doc = n.doc(n.paragraph('Hello world!')) const selection = extractSelection(doc) // undefined ```
(docconst doc: Node)?.toJSONSelection.toJSON(): any
Convert the selection to a JSON representation. When implementing this for a custom selection class, make sure to give the object a `type` property whose value matches the ID under which you [registered](https://prosemirror.net/docs/ref/#state.Selection^jsonID) your class.
(),
) })
import { unionfunction union<const E extends readonly Extension[]>(...exts: E): Union<E> (+1 overload)
Merges multiple extensions into one. You can pass multiple extensions as arguments or a single array containing multiple extensions.
@throwsIf no extensions are provided.@example```ts function defineFancyNodes() { return union( defineFancyParagraph(), defineFancyHeading(), ) } ```@example```ts function defineFancyNodes() { return union([ defineFancyParagraph(), defineFancyHeading(), ]) } ```
} from 'prosekit/core'
import { createTestEditorfunction createTestEditor<E extends Extension>(options: EditorOptions<E>): TestEditor<E> } from 'prosekit/core/test' import { defineBoldfunction defineBold(): BoldExtension } from 'prosekit/extensions/bold' import { defineDocfunction defineDoc(): DocExtension } from 'prosekit/extensions/doc' import { defineParagraphfunction defineParagraph(): ParagraphExtension
Defines a paragraph node. The paragraph node spec has the highest priority, because it should be the default block node for most cases.
} from 'prosekit/extensions/paragraph'
import { defineTextfunction defineText(): TextExtension } from 'prosekit/extensions/text' import { describeconst describe: SuiteAPI
Creates a suite of tests, allowing for grouping and hierarchical organization of tests. Suites can contain both tests and other suites, enabling complex test structures.
@paramname - The name of the suite, used for identification and reporting.@paramfn - A function that defines the tests and suites within this suite.@example```ts // Define a suite with two tests describe('Math operations', () => { test('should add two numbers', () => { expect(add(1, 2)).toBe(3); }); test('should subtract two numbers', () => { expect(subtract(5, 2)).toBe(3); }); }); ```@example```ts // Define nested suites describe('String operations', () => { describe('Trimming', () => { test('should trim whitespace from start and end', () => { expect(' hello '.trim()).toBe('hello'); }); }); describe('Concatenation', () => { test('should concatenate two strings', () => { expect('hello' + ' ' + 'world').toBe('hello world'); }); }); }); ```
, expectconst expect: ExpectStatic, itconst it: TestAPI
Defines a test case with a given name and test function. The test function can optionally be configured with test options.
@paramname - The name of the test or a function that will be used as a test name.@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.@throws{Error} If called inside another test function.@example```ts // Define a simple test it('adds two numbers', () => { expect(add(1, 2)).toBe(3); }); ```@example```ts // Define a test with options it('subtracts two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); }); ```
} from 'vitest'
describedescribe<object>(name: string | Function, fn?: SuiteFactory<object> | undefined, options?: number): SuiteCollector<object> (+1 overload)
Creates a suite of tests, allowing for grouping and hierarchical organization of tests. Suites can contain both tests and other suites, enabling complex test structures.
@paramname - The name of the suite, used for identification and reporting.@paramfn - A function that defines the tests and suites within this suite.@example```ts // Define a suite with two tests describe('Math operations', () => { test('should add two numbers', () => { expect(add(1, 2)).toBe(3); }); test('should subtract two numbers', () => { expect(subtract(5, 2)).toBe(3); }); }); ```@example```ts // Define nested suites describe('String operations', () => { describe('Trimming', () => { test('should trim whitespace from start and end', () => { expect(' hello '.trim()).toBe('hello'); }); }); describe('Concatenation', () => { test('should concatenate two strings', () => { expect('hello' + ' ' + 'world').toBe('hello world'); }); }); }); ```
('toggleBold', () => {
itit<object>(name: string | Function, fn?: TestFunction<object> | undefined, options?: number): void (+1 overload)
Defines a test case with a given name and test function. The test function can optionally be configured with test options.
@paramname - The name of the test or a function that will be used as a test name.@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.@throws{Error} If called inside another test function.@example```ts // Define a simple test it('adds two numbers', () => { expect(add(1, 2)).toBe(3); }); ```@example```ts // Define a test with options it('subtracts two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); }); ```
('wraps the selection in a bold mark', () => {
const editorconst editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>> = createTestEditorcreateTestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>(options: EditorOptions<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>): TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>({ extensionEditorOptions<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>.extension: Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>
The extension to use when creating the editor.
: unionunion<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>(exts_0: DocExtension, exts_1: TextExtension, exts_2: ParagraphExtension, exts_3: BoldExtension): Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]> (+1 overload)
Merges multiple extensions into one. You can pass multiple extensions as arguments or a single array containing multiple extensions.
@throwsIf no extensions are provided.@example```ts function defineFancyNodes() { return union( defineFancyParagraph(), defineFancyHeading(), ) } ```@example```ts function defineFancyNodes() { return union([ defineFancyParagraph(), defineFancyHeading(), ]) } ```
(
defineDocfunction defineDoc(): DocExtension(), defineTextfunction defineText(): TextExtension(), defineParagraphfunction defineParagraph(): ParagraphExtension
Defines a paragraph node. The paragraph node spec has the highest priority, because it should be the default block node for most cases.
(),
defineBoldfunction defineBold(): BoldExtension(), ), }) const n
const n: ToNodeAction<SimplifyDeeper<{
    doc: {
        readonly [x: string]: any;
    };
    paragraph: {
        readonly [x: string]: any;
    };
    text: {
        readonly [x: string]: any;
    };
}>>
= editorconst editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>.nodes
Editor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>.nodes: ToNodeAction<SimplifyDeeper<{
    doc: {
        readonly [x: string]: any;
    };
    paragraph: {
        readonly [x: string]: any;
    };
    text: {
        readonly [x: string]: any;
    };
}>>
All {@link NodeAction } s defined by the editor.
editorconst editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>.setTestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>.set(doc: Node): void
Set the editor state to the given document. You can use special tokens `<a>` and `<b>` to set the anchor and head positions of the selection.
@example```ts const editor = createTestEditor({ extension }) const n = editor.nodes const doc = n.doc(n.paragraph('<a>Hello<b> world!')) editor.set(doc) // "Hello" is selected. ```
(n
const n: ToNodeAction<SimplifyDeeper<{
    doc: {
        readonly [x: string]: any;
    };
    paragraph: {
        readonly [x: string]: any;
    };
    text: {
        readonly [x: string]: any;
    };
}>>
.doc
doc: NodeAction
(attrs: {
    readonly [x: string]: any;
} | null, ...children: NodeChild[]) => Node (+1 overload)
Creates a node with attributes and any number of children.
(n
const n: ToNodeAction<SimplifyDeeper<{
    doc: {
        readonly [x: string]: any;
    };
    paragraph: {
        readonly [x: string]: any;
    };
    text: {
        readonly [x: string]: any;
    };
}>>
.paragraph
paragraph: NodeAction
(...children: NodeChild[]) => Node (+1 overload)
Creates a node with any number of children.
('<a>hi<b>')))
expectexpect<boolean>(actual: boolean, message?: string): Assertion<boolean> (+1 overload)(editorconst editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>.commands
Editor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>.commands: ToCommandAction<{
    toggleBold: [];
    setParagraph: [];
}>
All {@link CommandAction } s defined by the editor.
.toggleBoldtoggleBold: CommandAction<[]>.canExecCommandAction<[]>.canExec(): boolean
Check if the current command can be executed. Return `true` if the command can be executed, otherwise `false`.
()).toBeJestAssertion<boolean>.toBe: <boolean>(expected: boolean) => void
Checks that a value is what you expect. It calls `Object.is` to compare values. Don't use `toBe` with floating-point numbers.
@exampleexpect(result).toBe(42); expect(status).toBe(true);
(true)
editorconst editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>.commands
Editor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>.commands: ToCommandAction<{
    toggleBold: [];
    setParagraph: [];
}>
All {@link CommandAction } s defined by the editor.
.toggleBold
toggleBold: CommandAction
() => boolean
Execute the current command. Return `true` if the command was successfully executed, otherwise `false`.
()
expectexpect<boolean>(actual: boolean, message?: string): Assertion<boolean> (+1 overload)(editorconst editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>.marks
Editor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>.marks: ToMarkAction<SimplifyDeeper<{
    bold: {
        readonly [x: string]: any;
    };
}>>
All {@link MarkAction } s defined by the editor.
.bold
bold: MarkAction<{
    readonly [x: string]: any;
}>
.isActive
MarkAction<{ readonly [x: string]: any; }>.isActive: (attrs?: {
    readonly [x: string]: any;
} | undefined) => boolean
Checks if the mark is active in the current editor selection. If the optional `attrs` parameter is provided, it will check if the mark is active with the given attributes.
()).toBeJestAssertion<boolean>.toBe: <boolean>(expected: boolean) => void
Checks that a value is what you expect. It calls `Object.is` to compare values. Don't use `toBe` with floating-point numbers.
@exampleexpect(result).toBe(42); expect(status).toBe(true);
(true)
}) })