Testing your editor
prosekit/core/test provides a small layer for unit-testing extensions and editors without spinning up a browser.
createTestEditor
Section titled “createTestEditor”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 { union function 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 { createTestEditor function createTestEditor<E extends Extension>(options: EditorOptions<E>): TestEditor<E> } from 'prosekit/core/test'
import { defineDoc function defineDoc(): DocExtension } from 'prosekit/extensions/doc'
import { defineParagraph function defineParagraph(): ParagraphExtensionDefines 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 { defineText function defineText(): TextExtension } from 'prosekit/extensions/text'
const editor const editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>> = createTestEditor createTestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>(options: EditorOptions<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>): TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>> ({
extension EditorOptions<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>.extension: Union<readonly [DocExtension, TextExtension, ParagraphExtension]>The extension to use when creating the editor. : union union<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(),
])
}
``` (defineDoc function defineDoc(): DocExtension (), defineText function defineText(): TextExtension (), defineParagraph function defineParagraph(): ParagraphExtensionDefines 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.
Using the builder syntax
Section titled “Using the builder syntax”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 { union function 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 { createTestEditor function createTestEditor<E extends Extension>(options: EditorOptions<E>): TestEditor<E> } from 'prosekit/core/test'
import { defineBold function defineBold(): BoldExtension } from 'prosekit/extensions/bold'
import { defineDoc function defineDoc(): DocExtension } from 'prosekit/extensions/doc'
import { defineParagraph function defineParagraph(): ParagraphExtensionDefines 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 { defineText function defineText(): TextExtension } from 'prosekit/extensions/text'
const editor const editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>> = createTestEditor createTestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>(options: EditorOptions<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>): TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>> ({
extension EditorOptions<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>.extension: Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>The extension to use when creating the editor. : union union<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(),
])
}
``` (
defineDoc function defineDoc(): DocExtension (),
defineText function defineText(): TextExtension (),
defineParagraph function defineParagraph(): ParagraphExtensionDefines a paragraph node.
The paragraph node spec has the highest priority, because it should be the
default block node for most cases. (),
defineBold function defineBold(): BoldExtension (),
),
})
const n const n: ToNodeAction<SimplifyDeeper<{
doc: {
readonly [x: string]: any;
};
paragraph: {
readonly [x: string]: any;
};
text: {
readonly [x: string]: any;
};
}>>
= editor const 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;
};
}>>
= editor const 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 doc const 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'), '!'),
)
editor const editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>> .set TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>.set(doc: Node): voidSet 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.
``` (doc const doc: Node )Building documents without an editor
Section titled “Building documents without an editor”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 { createMarkBuilders function 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('!')))
``` , createNodeBuilders function 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)
``` , union function 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 { defineBold function defineBold(): BoldExtension } from 'prosekit/extensions/bold'
import { defineDoc function defineDoc(): DocExtension } from 'prosekit/extensions/doc'
import { defineHeading function defineHeading(): HeadingExtension } from 'prosekit/extensions/heading'
import { defineParagraph function defineParagraph(): ParagraphExtensionDefines 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 { defineText function defineText(): TextExtension } from 'prosekit/extensions/text'
function defineTestExtension function defineTestExtension(): Union<readonly [DocExtension, TextExtension, ParagraphExtension, HeadingExtension, BoldExtension]> () {
return union union<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(),
])
}
``` (
defineDoc function defineDoc(): DocExtension (),
defineText function defineText(): TextExtension (),
defineParagraph function defineParagraph(): ParagraphExtensionDefines a paragraph node.
The paragraph node spec has the highest priority, because it should be the
default block node for most cases. (),
defineHeading function defineHeading(): HeadingExtension (),
defineBold function 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: [];
};
}>
= ReturnType type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : anyObtain the return type of a function type <typeof defineTestExtension function defineTestExtension(): Union<readonly [DocExtension, TextExtension, ParagraphExtension, HeadingExtension, BoldExtension]> >
const extension const 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: [];
};
}>
= defineTestExtension function defineTestExtension(): Union<readonly [DocExtension, TextExtension, ParagraphExtension, HeadingExtension, BoldExtension]> ()
const schema const schema: Schema<any, any> = extension const extension: Union<readonly [DocExtension, TextExtension, ParagraphExtension, HeadingExtension, BoldExtension]> .schema Extension<T extends ExtensionTyping<any, any, any> = ExtensionTyping<any, any, any>>.schema: Schema<any, any> | nullThe 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: [];
};
}>
>(schema const 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: [];
};
}>
>(schema const schema: Schema<any, any> )
const doc const 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. ({ level level: 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'), '!'),
)Selection markers
Section titled “Selection markers”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 { union function 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 { createTestEditor function createTestEditor<E extends Extension>(options: EditorOptions<E>): TestEditor<E> } from 'prosekit/core/test'
import { defineDoc function defineDoc(): DocExtension } from 'prosekit/extensions/doc'
import { defineParagraph function defineParagraph(): ParagraphExtensionDefines 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 { defineText function defineText(): TextExtension } from 'prosekit/extensions/text'
const editor const editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>> = createTestEditor createTestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>(options: EditorOptions<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>): TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>> ({
extension EditorOptions<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>.extension: Union<readonly [DocExtension, TextExtension, ParagraphExtension]>The extension to use when creating the editor. : union union<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(),
])
}
``` (defineDoc function defineDoc(): DocExtension (), defineText function defineText(): TextExtension (), defineParagraph function defineParagraph(): ParagraphExtensionDefines 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;
};
}>>
= editor const 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 doc const 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!'))
editor const editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>> .set TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>.set(doc: Node): voidSet 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.
``` (doc const 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 { union function 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 { createTestEditor function createTestEditor<E extends Extension>(options: EditorOptions<E>): TestEditor<E> , extractSelection function extractSelection(doc: Node): Selection | undefinedExtracts 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 { defineDoc function defineDoc(): DocExtension } from 'prosekit/extensions/doc'
import { defineParagraph function defineParagraph(): ParagraphExtensionDefines 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 { defineText function defineText(): TextExtension } from 'prosekit/extensions/text'
import { expect const expect: ExpectStatic , it const it: TestAPIDefines 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 editor const editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>> = createTestEditor createTestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>(options: EditorOptions<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>): TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>> ({
extension EditorOptions<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>.extension: Union<readonly [DocExtension, TextExtension, ParagraphExtension]>The extension to use when creating the editor. : union union<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(),
])
}
``` (defineDoc function defineDoc(): DocExtension (), defineText function defineText(): TextExtension (), defineParagraph function defineParagraph(): ParagraphExtensionDefines 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;
};
}>>
= editor const 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.
it it<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 doc const 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!'))
editor const editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>> .set TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>.set(doc: Node): voidSet 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.
``` (doc const doc: Node )
expect expect<any>(actual: any, message?: string): Assertion<any> (+1 overload) (editor const editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>> .state Editor<Union<readonly [DocExtension, TextExtension, ParagraphExtension]>>.state: EditorStateThe editor's current state. .selection EditorState.selection: SelectionThe selection. .toJSON Selection.toJSON(): anyConvert 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. ()).toEqual JestAssertion<any>.toEqual: <any>(expected: any) => voidUsed 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 }); (
extractSelection function extractSelection(doc: Node): Selection | undefinedExtracts 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
``` (doc const doc: Node )?.toJSON Selection.toJSON(): anyConvert 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. (),
)
})A complete unit test
Section titled “A complete unit test”import { union function 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 { createTestEditor function createTestEditor<E extends Extension>(options: EditorOptions<E>): TestEditor<E> } from 'prosekit/core/test'
import { defineBold function defineBold(): BoldExtension } from 'prosekit/extensions/bold'
import { defineDoc function defineDoc(): DocExtension } from 'prosekit/extensions/doc'
import { defineParagraph function defineParagraph(): ParagraphExtensionDefines 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 { defineText function defineText(): TextExtension } from 'prosekit/extensions/text'
import { describe const describe: SuiteAPICreates 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');
});
});
});
``` , expect const expect: ExpectStatic , it const it: TestAPIDefines 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'
describe describe<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', () => {
it it<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 editor const editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>> = createTestEditor createTestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>(options: EditorOptions<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>): TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>> ({
extension EditorOptions<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>.extension: Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>The extension to use when creating the editor. : union union<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(),
])
}
``` (
defineDoc function defineDoc(): DocExtension (),
defineText function defineText(): TextExtension (),
defineParagraph function defineParagraph(): ParagraphExtensionDefines a paragraph node.
The paragraph node spec has the highest priority, because it should be the
default block node for most cases. (),
defineBold function defineBold(): BoldExtension (),
),
})
const n const n: ToNodeAction<SimplifyDeeper<{
doc: {
readonly [x: string]: any;
};
paragraph: {
readonly [x: string]: any;
};
text: {
readonly [x: string]: any;
};
}>>
= editor const 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.
editor const editor: TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>> .set TestEditor<Union<readonly [DocExtension, TextExtension, ParagraphExtension, BoldExtension]>>.set(doc: Node): voidSet 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>')))
expect expect<boolean>(actual: boolean, message?: string): Assertion<boolean> (+1 overload) (editor const 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<[]> .canExec CommandAction<[]>.canExec(): booleanCheck if the current command can be executed. Return `true` if the command
can be executed, otherwise `false`. ()).toBe JestAssertion<boolean>.toBe: <boolean>(expected: boolean) => voidChecks 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)
editor const 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`. ()
expect expect<boolean>(actual: boolean, message?: string): Assertion<boolean> (+1 overload) (editor const 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. ()).toBe JestAssertion<boolean>.toBe: <boolean>(expected: boolean) => voidChecks 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)
})
})