jest-prosemirror
package jest-prosemirror
Write expressive tests for your prosemirror editor
Remarks:
## The problem
You want to write tests for some of your prosemirror editor but you don't know where to start. You know you should avoid testing implementation details and just want to be sure that your commands and plugins produce the correct underlying prosemirror state.
## This solution
jest-prosemirror
takes inspiration from the [testing-library
](https://github.com/testing-library/react-testing-library) mantra and enables you to write more intuitive tests for your prosemirror editor.
## Installation
yarn add jest-prosemirror # yarn
pnpm add jest-prosemirror # pnpm
npm install jest-prosemirror # npm
## Getting started
### Quick setup
For a quick setup add the following to your jest.config.js file.
module.exports = {
setupFilesAfterEnv: ['jest-prosemirror/environment'],
testEnvironment: 'jsdom', // Required for dom manipulation
};
This will automatically:
- Add the jest assertions
toTransformNode
andtoEqualProsemirrorNode
.
If you are using typescript then add this to your tsconfig.json
file for global type support.
{
"compilerOptions": {
"types": ["jest-prosemirror"]
}
}
### Manual setup
Create a jest.framework.dom.ts
file and add the following
import { prosemirrorMatchers } from 'jest-prosemirror';
// Add jest-prosemirror assertions
expect.extend(prosemirrorMatchers);
In your jest.config.js
add the created file to your configuration.
module.exports = {
setupFilesAfterEnv: ['<rootDir>/jest.framework.dom.ts'],
testEnvironment: 'jsdom', // Required for dom manipulation
};
## Snapshot serializer
This package exports a serializer for better snapshot testing of prosemirror primitives. To set this up add the following to your jest.config.js
file.
module.exports = {
snapshotSerializers: ['jest-prosemirror/serializer'],
};
Alternatively, you can add the following to your jest.framework.dom.ts
file.
import { prosemirrorSerializer } from 'jest-prosemirror';
// Add the serializer for use throughout all the configured test files.
expect.addSnapshotSerializer(prosemirrorSerializer);
class ProsemirrorTestChain
An instance of this class is returned when using createEditor
. It allows for chaining of test operations and adds some useful helpers to your testing toolkit.
Signature:
export declare class ProsemirrorTestChain
ProsemirrorTestChain.(constructor)
Constructs a new instance of the ProsemirrorTestChain
class
Signature:
constructor(view: TestEditorView);
Parameters:
Parameter | Type | Description |
---|---|---|
view | TestEditorView |
property copied
The content to write to the clipboard if copy the current selection.
Signature:
get copied(): {
text: string;
html: string;
};
property debug
Logs to the dom for help debugging your tests.
Signature:
debug: () => this;
property doc
The current prosemirror node document
Signature:
get doc(): ProsemirrorNode;
property end
The end of the current selection.
Signature:
get end(): number;
property remirrorCommand
Takes any remirror command as an input and dispatches it within the document context.
Signature:
readonly remirrorCommand: (command: CommandFunction) => this;
property schema
The prosemirror schema.
Signature:
get schema(): Schema;
property selection
The prosemirror selection.
Signature:
get selection(): Selection;
property selectText
Selects the text between the provided selection primitive.
Signature:
readonly selectText: (selection: PrimitiveSelection) => this;
property start
The start of the current selection.
Signature:
get start(): number;
property state
The prosemirror state.
Signature:
get state(): EditorState;
property view
The prosemirror view.
Signature:
view: TestEditorView;
method backspace
Simulates a backspace keypress and deletes text backwards.
Signature:
backspace(times?: number): this;
Parameters:
Parameter | Type | Description |
---|---|---|
times | number | (Optional) |
Returns:
this
method callback
Callback function which receives the start
, end
, state
, view
, schema
and selection
properties and allows for easier testing of the current state of the editor.
Signature:
callback(fn: (content: ReturnValueCallbackProps) => void): this;
Parameters:
Parameter | Type | Description |
---|---|---|
fn | (content: ReturnValueCallbackProps) => void |
Returns:
this
method command
Run the command within the prosemirror editor.
Signature:
command(command: ProsemirrorCommandFunction): this;
Parameters:
Parameter | Type | Description |
---|---|---|
command | ProsemirrorCommandFunction | the command function to run |
Returns:
this
Remarks:
test('commands are run', () => {
createEditor(doc(p('<cursor>')))
.command((state, dispatch) => {
if (dispatch) {
dispatch(state.tr.insertText('hello'));
}
})
.callback(content => {
expect(content.state.doc).toEqualProsemirrorDocument(doc(p('hello')));
})
})
method fire
Fire an event in the editor (very hit and miss).
Signature:
fire(props: Omit<FireEventAtPositionProps, 'view'>): this;
Parameters:
Parameter | Type | Description |
---|---|---|
props | Omit<FireEventAtPositionProps, 'view'> | the props when firing an event |
Returns:
this
method insertText
Insert text into the editor at the current position.
Signature:
insertText(text: string): this;
Parameters:
Parameter | Type | Description |
---|---|---|
text | string | the text to insert |
Returns:
this
method of
A static helper utility for creating new TestReturn values.
Signature:
static of(view: TestEditorView): ProsemirrorTestChain;
Parameters:
Parameter | Type | Description |
---|---|---|
view | TestEditorView |
Returns:
method overwrite
Overwrite all the current content within the editor.
Signature:
overwrite(newDocument: ProsemirrorNode): this;
Parameters:
Parameter | Type | Description |
---|---|---|
newDocument | ProsemirrorNode |
Returns:
this
method paste
Paste text into the editor.
Signature:
paste(content: ProsemirrorNode | string | {
text: string;
html: string;
plainText?: boolean;
}): this;
Parameters:
Parameter | Type | Description |
---|---|---|
content | ProsemirrorNode | string | { text: string; html: string; plainText?: boolean; } |
Returns:
this
method press
Simulate a keypress which is run through the editor's key handlers.
**NOTE** This only simulates the events. For example an Enter
would run all enter key handlers but not actually create a new line.
Signature:
press(char: string): this;
Parameters:
Parameter | Type | Description |
---|---|---|
char | string | the character to type |
Returns:
this
method shortcut
Type a keyboard shortcut - e.g. Mod-Enter
.
**NOTE** This only simulates the events. For example an Mod-Enter
would run all enter key handlers but not actually create a new line.
Signature:
shortcut(mod: string): this;
Parameters:
Parameter | Type | Description |
---|---|---|
mod | string | the keyboard shortcut to type |
Returns:
this
function apply()
Apply the command to the prosemirror node passed in.
Returns a tuple matching the following structure [ bool => was the command successfully applied taggedDoc => the new doc as a result of the command state => The new editor state after applying the command ]
Signature:
export declare function apply(taggedDocument: ProsemirrorNode, command: ProsemirrorCommandFunction, result?: ProsemirrorNode): ApplyReturn;
Parameters:
Parameter | Type | Description |
---|---|---|
taggedDocument | ProsemirrorNode | |
command | ProsemirrorCommandFunction | |
result | ProsemirrorNode | (Optional) |
Returns:
function backspace()
Simulate a backspace key press.
Signature:
export declare function backspace(props: BackspaceProps): void;
Parameters:
Parameter | Type | Description |
---|---|---|
props | BackspaceProps |
Returns:
void
function createEditor()
Create a test prosemirror editor an pass back helper properties and methods.
Signature:
export declare function createEditor(taggedDocument: ProsemirrorNode, options?: CreateEditorOptions): ProsemirrorTestChain;
Parameters:
Parameter | Type | Description |
---|---|---|
taggedDocument | ProsemirrorNode | |
options | CreateEditorOptions | (Optional) the CreateEditorOptions interface which includes all DirectEditorProps except for state . |
Returns:
Remarks:
The call to create editor can be chained with various commands to enable testing of the editor at each step along it's state without the need for intermediate holding variables.
The created editor is automatically cleaned after each test.
import { createEditor } from 'jest-remirror';
test('`keyBindings`', () => {
const keyBindings = {
Enter: jest.fn((params: SuggestKeyBindingProps) => {
params.command();
}),
};
const plugin = suggest({char: '@', name: 'at', keyBindings, matchOffset: 0,
createCommand: ({ view }) => () =>
view.dispatch(view.state.tr.insertText('awesome')),
});
createEditor(doc(p('<cursor>')), { plugins: [plugin] }) .insertText('@')
.press('Enter')
.callback(content => {
expect(content.state.doc).toEqualProsemirrorNode(doc(p('@awesome')));
});
});
function createEvents()
Signature:
export declare function createEvents<CreatedEvent extends Event>(event: EventType, options: Shape): CreatedEvent[];
Parameters:
Parameter | Type | Description |
---|---|---|
event | EventType | |
options | Shape |
Returns:
CreatedEvent[]
function createState()
Create the editor state for a tagged prosemirror doc
Signature:
export declare function createState(taggedDoc: ProsemirrorNode, plugins?: ProsemirrorPlugin[]): EditorState;
Parameters:
Parameter | Type | Description |
---|---|---|
taggedDoc | ProsemirrorNode | |
plugins | ProsemirrorPlugin[] | (Optional) |
Returns:
EditorState
function dispatchAllSelection()
Select everything in the current doc.
Signature:
export declare function dispatchAllSelection(view: TestEditorView): void;
Parameters:
Parameter | Type | Description |
---|---|---|
view | TestEditorView |
Returns:
void
function dispatchAnchorTextSelection()
Dispatch a text selection from start to [end]
Signature:
export declare function dispatchAnchorTextSelection(props: DispatchAnchorTextSelectionProps): void;
Parameters:
Parameter | Type | Description |
---|---|---|
props | DispatchAnchorTextSelectionProps |
Returns:
void
function dispatchCellSelection()
Signature:
export declare function dispatchCellSelection(props: DispatchNodeSelectionProps): void;
Parameters:
Parameter | Type | Description |
---|---|---|
props | DispatchNodeSelectionProps |
Returns:
void
function dispatchNodeSelection()
Dispatch a text selection from the provided pos.
Signature:
export declare function dispatchNodeSelection(props: DispatchNodeSelectionProps): void;
Parameters:
Parameter | Type | Description |
---|---|---|
props | DispatchNodeSelectionProps |
Returns:
void
function dispatchTextSelection()
Dispatch a text selection from start to [end]
Signature:
export declare function dispatchTextSelection(props: DispatchTextSelectionProps): void;
Parameters:
Parameter | Type | Description |
---|---|---|
props | DispatchTextSelectionProps |
Returns:
void
function findTextNode()
Find the first text node with the provided string.
Signature:
export declare function findTextNode(node: Node, text: string): Node | undefined;
Parameters:
Parameter | Type | Description |
---|---|---|
node | Node | |
text | string |
Returns:
Node | undefined
function fireEventAtPosition()
Fires an event at the provided position or the current selected position in the dom.
Signature:
export declare function fireEventAtPosition(props: FireEventAtPositionProps): void;
Parameters:
Parameter | Type | Description |
---|---|---|
props | FireEventAtPositionProps |
Returns:
void
function flush()
Flushes the dom
Signature:
export declare function flush(view: TestEditorView): void;
Parameters:
Parameter | Type | Description |
---|---|---|
view | TestEditorView |
Returns:
void
function forwardDelete()
Simulate a backspace key press.
Signature:
export declare function forwardDelete(props: BackspaceProps): void;
Parameters:
Parameter | Type | Description |
---|---|---|
props | BackspaceProps |
Returns:
void
function initSelection()
Initialize the selection based on the passed in tagged node via it's cursor.
The supported tags are ['cursor', 'node', 'start', 'end', 'anchor', 'all']
Signature:
export declare function initSelection(taggedDoc: ProsemirrorNode): Selection | undefined;
Parameters:
Parameter | Type | Description |
---|---|---|
taggedDoc | ProsemirrorNode |
Returns:
Selection | undefined
function insertText()
Insert text from the provided index. Each key is entered individually to better simulate calls to handleTextInput.
Signature:
export declare function insertText(props: InsertTextProps): void;
Parameters:
Parameter | Type | Description |
---|---|---|
props | InsertTextProps |
Returns:
void
function pmBuild()
A short hand way for building prosemirror test builders with the core nodes already provided - doc
- paragraph
| 'p' - text
Signature:
export declare function pmBuild<Types extends BuilderTypes = BuilderTypes>(testSchema: EditorSchema, names: Types): BuilderReturns<Types & DefaultBuilderTypes>;
Parameters:
Parameter | Type | Description |
---|---|---|
testSchema | EditorSchema | The schema to use which provided a doc, paragraph and text schema |
names | Types | the extra marks and nodes to provide with their attributes |
Returns:
BuilderReturns<Types & DefaultBuilderTypes>
function press()
Press a key.
Signature:
export declare function press(props: PressProps): void;
Parameters:
Parameter | Type | Description |
---|---|---|
props | PressProps |
Returns:
void
function selectionFor()
Returns a selection regardless of whether anything is tagged in the provided doc
Signature:
export declare function selectionFor(taggedDoc: ProsemirrorNode): Selection;
Parameters:
Parameter | Type | Description |
---|---|---|
taggedDoc | ProsemirrorNode |
Returns:
Selection
function shortcut()
Runs a keyboard shortcut.
Signature:
export declare function shortcut(props: KeyboardShortcutProps): void;
Parameters:
Parameter | Type | Description |
---|---|---|
props | KeyboardShortcutProps |
Returns:
void
function taggedDocHasSelection()
Checks that the tagged doc has a selection
Signature:
export declare function taggedDocHasSelection(taggedDoc: ProsemirrorNode): boolean;
Parameters:
Parameter | Type | Description |
---|---|---|
taggedDoc | ProsemirrorNode |
Returns:
boolean
variable a
Signature:
a: pm.MarkBuilder
variable atomBlock
Signature:
atomBlock: pm.NodeBuilder
variable atomContainer
Signature:
atomContainer: pm.NodeBuilder
variable atomInline
Signature:
atomInline: pm.NodeBuilder
variable blockquote
Signature:
blockquote: pm.NodeBuilder
variable br
Signature:
br: NodeBuilder
variable code_block
Signature:
code_block: NodeBuilder
variable code
Signature:
code: pm.MarkBuilder
variable codeBlock
Signature:
codeBlock: NodeBuilder
variable doc
Signature:
doc: pm.NodeBuilder
variable em
Signature:
em: pm.MarkBuilder
variable h1
Signature:
h1: pm.NodeBuilder
variable h2
Signature:
h2: pm.NodeBuilder
variable h3
Signature:
h3: pm.NodeBuilder
variable h4
Signature:
h4: pm.NodeBuilder
variable h5
Signature:
h5: pm.NodeBuilder
variable h6
Signature:
h6: pm.NodeBuilder
variable hardBreak
Signature:
hardBreak: NodeBuilder
variable heading
Signature:
heading: NodeBuilder
variable horizontalRule
Signature:
horizontalRule: NodeBuilder
variable hr
Signature:
hr: NodeBuilder
variable image
Signature:
image: NodeBuilder
variable img
Signature:
img: NodeBuilder
variable li
Signature:
li: pm.NodeBuilder
variable link
Signature:
link: pm.MarkBuilder
variable ol
Signature:
ol: pm.NodeBuilder
variable p
Signature:
p: pm.NodeBuilder
variable paragraph
Signature:
paragraph: pm.NodeBuilder
variable parseFromClipboard
Signature:
parseFromClipboard: (view: EditorView, text: string, html: string | null, plainText: boolean, $context: ResolvedPos) => Slice | null
variable pre
Signature:
pre: NodeBuilder
variable prosemirrorMatchers
Signature:
prosemirrorMatchers: jest.ExpectExtendMap
variable prosemirrorSerializer
Jest serializer for prosemirror nodes and the editor state.
Signature:
prosemirrorSerializer: jest.SnapshotSerializerPlugin
variable schema
Signature:
schema: Schema<"doc" | "paragraph" | "text" | "blockquote" | "heading" | "code_block" | "hard_break" | "image" | "table" | "table_row" | "table_cell" | "table_header" | "atomInline" | "atomBlock" | "containerWithRestrictedContent" | "orderedList" | "bulletList" | "listItem" | "horizontalRule" | "atomContainer", "link" | "em" | "strong" | "code" | "strike">
variable serializeForClipboard
Signature:
serializeForClipboard: (view: EditorView, slice: Slice) => {
dom: HTMLDivElement;
text: string;
}
variable setupProsemirrorEnvironment
Setup the environment automatically for jest-prosemirror
Signature:
setupProsemirrorEnvironment: () => void
variable strong
Signature:
strong: pm.MarkBuilder
variable table
Signature:
table: pm.NodeBuilder
variable tableCell
Signature:
tableCell: pm.NodeBuilder
variable tableHeaderCell
Signature:
tableHeaderCell: pm.NodeBuilder
variable tableRow
Signature:
tableRow: pm.NodeBuilder
variable td
Signature:
td: pm.NodeBuilder
variable tdCursor
Signature:
tdCursor: ProsemirrorNode
variable tdEmpty
Signature:
tdEmpty: ProsemirrorNode
variable text
Signature:
text: pm.NodeBuilder
variable th
Signature:
th: pm.NodeBuilder
variable thCursor
Signature:
thCursor: ProsemirrorNode
variable thEmpty
Signature:
thEmpty: ProsemirrorNode
variable tr
Signature:
tr: pm.NodeBuilder
variable ul
Signature:
ul: pm.NodeBuilder
interface ApplyReturn
The return type for the apply method which
Signature:
export interface ApplyReturn extends TaggedDocProps, EditorStateProps
Extends: TaggedDocProps, EditorStateProps
Remarks:
(Some inherited members may not be shown because they are not represented in the documentation.)
property pass
True when the command was applied successfully.
Signature:
pass: boolean;
property taggedDoc
A tagged ProsemirrorNode which can hold cursor information from the passed in text.
Signature:
taggedDoc: ProsemirrorNode;
interface CommandTransformation
Tests that a command run transform the nodes from one state to another. The second state is optional if nothing has changed.
Signature:
export interface CommandTransformation
property from
The initial prosemirror node.
import { doc, p, strong} from 'jest-prosemirror';
const from = doc(p('Hello ', strong('Friend')));
Signature:
from: ProsemirrorNode;
property to
The output of the command transformation.
import { doc, p, strong} from 'jest-prosemirror';
const to = doc(p(strong('Friend')));
This is optional and can be omitted if the transformation doesn't produce any results.
Signature:
to?: ProsemirrorNode;
interface CreateEditorOptions
Signature:
export interface CreateEditorOptions extends Omit<DirectEditorProps, 'state' | 'plugins'>
Extends: Omit<DirectEditorProps, 'state' | 'plugins'>
(Some inherited members may not be shown because they are not represented in the documentation.)
property autoClean
Whether to auto remove the editor from the dom after each test. It is advisable to leave this unchanged.
Signature:
autoClean?: boolean;
property plugins
The plugins that the test editor should use.
Signature:
plugins?: ProsemirrorPlugin[];
property rules
The input rules that the test editor should use.
Signature:
rules?: InputRule[];
interface FireProps
Signature:
export interface FireProps
property event
The event to fire on the view
Signature:
event: EventType;
property options
Options passed into the event
Signature:
options?: Shape;
property position
Override the default position to use
Signature:
position?: number;
interface InsertTextProps
Signature:
export interface InsertTextProps extends TestEditorViewProps, TextProps
Extends: TestEditorViewProps, TextProps
(Some inherited members may not be shown because they are not represented in the documentation.)
property start
The start point of text insertion
Signature:
start: number;
property view
An instance of the test editor view which allows for dispatching events and also containers TaggedProsemirrorNodes
Signature:
view: TestEditorView;
interface ReturnValueCallbackProps
Signature:
export interface ReturnValueCallbackProps extends TestEditorViewProps, EditorStateProps, SelectionProps
Extends: TestEditorViewProps, EditorStateProps, SelectionProps
(Some inherited members may not be shown because they are not represented in the documentation.)
property debug
Pretty log the current view to the dom.
Signature:
debug: () => void;
property doc
Signature:
doc: ProsemirrorNode;
property end
Signature:
end: number;
property schema
Signature:
schema: Schema;
property start
Signature:
start: number;
property view
An instance of the test editor view which allows for dispatching events and also containers TaggedProsemirrorNodes
Signature:
view: TestEditorView;
interface TaggedDocProps
Signature:
export interface TaggedDocProps
property taggedDoc
A tagged ProsemirrorNode which can hold cursor information from the passed in text.
Signature:
taggedDoc: ProsemirrorNode;
interface TaggedProsemirrorNode
Signature:
export interface TaggedProsemirrorNode extends TaggedFlatObject, ProsemirrorNode
Extends: TaggedFlatObject, ProsemirrorNode
(Some inherited members may not be shown because they are not represented in the documentation.)
interface TestEditorView
Signature:
export interface TestEditorView extends EditorView
Extends: EditorView
(Some inherited members may not be shown because they are not represented in the documentation.)
property dispatchEvent
Signature:
dispatchEvent: (event: string | CustomEvent | {
type: string;
}) => void;
property domObserver
Signature:
domObserver: {
flush: () => void;
};
interface TestEditorViewProps
Signature:
export interface TestEditorViewProps
property view
An instance of the test editor view which allows for dispatching events and also containers TaggedProsemirrorNodes
Signature:
view: TestEditorView;
type EventType
Signature:
export type EventType = keyof typeof rawEventMap | 'doubleClick' | 'tripleClick';