2021-06-08 01:23:25 +05:30
|
|
|
|
import { Node } from '@tiptap/core';
|
2021-09-04 01:27:46 +05:30
|
|
|
|
import { Document } from '@tiptap/extension-document';
|
|
|
|
|
import { Paragraph } from '@tiptap/extension-paragraph';
|
|
|
|
|
import { Text } from '@tiptap/extension-text';
|
|
|
|
|
import { Editor } from '@tiptap/vue-2';
|
2021-09-30 23:02:18 +05:30
|
|
|
|
import { builders, eq } from 'prosemirror-test-builder';
|
2021-10-27 15:23:28 +05:30
|
|
|
|
import { nextTick } from 'vue';
|
2023-01-13 00:05:48 +05:30
|
|
|
|
import Audio from '~/content_editor/extensions/audio';
|
|
|
|
|
import Blockquote from '~/content_editor/extensions/blockquote';
|
|
|
|
|
import Bold from '~/content_editor/extensions/bold';
|
|
|
|
|
import BulletList from '~/content_editor/extensions/bullet_list';
|
|
|
|
|
import Code from '~/content_editor/extensions/code';
|
|
|
|
|
import CodeBlockHighlight from '~/content_editor/extensions/code_block_highlight';
|
|
|
|
|
import DescriptionItem from '~/content_editor/extensions/description_item';
|
|
|
|
|
import DescriptionList from '~/content_editor/extensions/description_list';
|
|
|
|
|
import Details from '~/content_editor/extensions/details';
|
|
|
|
|
import DetailsContent from '~/content_editor/extensions/details_content';
|
|
|
|
|
import Emoji from '~/content_editor/extensions/emoji';
|
|
|
|
|
import FootnoteDefinition from '~/content_editor/extensions/footnote_definition';
|
|
|
|
|
import FootnoteReference from '~/content_editor/extensions/footnote_reference';
|
|
|
|
|
import FootnotesSection from '~/content_editor/extensions/footnotes_section';
|
|
|
|
|
import Frontmatter from '~/content_editor/extensions/frontmatter';
|
|
|
|
|
import Figure from '~/content_editor/extensions/figure';
|
|
|
|
|
import FigureCaption from '~/content_editor/extensions/figure_caption';
|
|
|
|
|
import HardBreak from '~/content_editor/extensions/hard_break';
|
|
|
|
|
import Heading from '~/content_editor/extensions/heading';
|
|
|
|
|
import HorizontalRule from '~/content_editor/extensions/horizontal_rule';
|
|
|
|
|
import Highlight from '~/content_editor/extensions/highlight';
|
|
|
|
|
import Image from '~/content_editor/extensions/image';
|
|
|
|
|
import InlineDiff from '~/content_editor/extensions/inline_diff';
|
|
|
|
|
import Italic from '~/content_editor/extensions/italic';
|
|
|
|
|
import Link from '~/content_editor/extensions/link';
|
|
|
|
|
import ListItem from '~/content_editor/extensions/list_item';
|
|
|
|
|
import OrderedList from '~/content_editor/extensions/ordered_list';
|
|
|
|
|
import ReferenceDefinition from '~/content_editor/extensions/reference_definition';
|
|
|
|
|
import Strike from '~/content_editor/extensions/strike';
|
|
|
|
|
import Table from '~/content_editor/extensions/table';
|
|
|
|
|
import TableCell from '~/content_editor/extensions/table_cell';
|
|
|
|
|
import TableHeader from '~/content_editor/extensions/table_header';
|
|
|
|
|
import TableRow from '~/content_editor/extensions/table_row';
|
|
|
|
|
import TableOfContents from '~/content_editor/extensions/table_of_contents';
|
|
|
|
|
import TaskItem from '~/content_editor/extensions/task_item';
|
|
|
|
|
import TaskList from '~/content_editor/extensions/task_list';
|
|
|
|
|
import Video from '~/content_editor/extensions/video';
|
|
|
|
|
import HTMLMarks from '~/content_editor/extensions/html_marks';
|
|
|
|
|
import HTMLNodes from '~/content_editor/extensions/html_nodes';
|
2021-09-30 23:02:18 +05:30
|
|
|
|
|
|
|
|
|
export const createDocBuilder = ({ tiptapEditor, names = {} }) => {
|
|
|
|
|
const docBuilders = builders(tiptapEditor.schema, {
|
|
|
|
|
p: { nodeType: 'paragraph' },
|
|
|
|
|
...names,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return { eq, builders: docBuilders };
|
|
|
|
|
};
|
2021-06-08 01:23:25 +05:30
|
|
|
|
|
2021-10-27 15:23:28 +05:30
|
|
|
|
export const emitEditorEvent = ({ tiptapEditor, event, params = {} }) => {
|
|
|
|
|
tiptapEditor.emit(event, { editor: tiptapEditor, ...params });
|
|
|
|
|
|
|
|
|
|
return nextTick();
|
|
|
|
|
};
|
|
|
|
|
|
2021-09-04 01:27:46 +05:30
|
|
|
|
/**
|
|
|
|
|
* Creates an instance of the Tiptap Editor class
|
|
|
|
|
* with a minimal configuration for testing purposes.
|
|
|
|
|
*
|
|
|
|
|
* It only includes the Document, Text, and Paragraph
|
|
|
|
|
* extensions.
|
|
|
|
|
*
|
|
|
|
|
* @param {Array} config.extensions One or more extensions to
|
|
|
|
|
* include in the editor
|
|
|
|
|
* @returns An instance of a Tiptap’s Editor class
|
|
|
|
|
*/
|
2021-09-30 23:02:18 +05:30
|
|
|
|
export const createTestEditor = ({ extensions = [] } = {}) => {
|
2021-09-04 01:27:46 +05:30
|
|
|
|
return new Editor({
|
|
|
|
|
extensions: [Document, Text, Paragraph, ...extensions],
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const mockChainedCommands = (editor, commandNames = []) => {
|
|
|
|
|
const commandMocks = commandNames.reduce(
|
|
|
|
|
(accum, commandName) => ({
|
|
|
|
|
...accum,
|
|
|
|
|
[commandName]: jest.fn(),
|
|
|
|
|
}),
|
|
|
|
|
{},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
Object.keys(commandMocks).forEach((commandName) => {
|
|
|
|
|
commandMocks[commandName].mockReturnValue(commandMocks);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
jest.spyOn(editor, 'chain').mockImplementation(() => commandMocks);
|
|
|
|
|
|
|
|
|
|
return commandMocks;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates a Content Editor extension for testing
|
|
|
|
|
* purposes.
|
|
|
|
|
*
|
|
|
|
|
* @param {Array} config.commands A list of command names
|
|
|
|
|
* to include in the test extension. This utility will create
|
|
|
|
|
* Jest mock functions for each command name.
|
|
|
|
|
* @returns An object with the following properties:
|
|
|
|
|
*
|
|
|
|
|
* tiptapExtension A Node tiptap extension
|
|
|
|
|
* commandMocks Jest mock functions for each created command
|
|
|
|
|
* serializer A markdown serializer for the extension
|
|
|
|
|
*/
|
|
|
|
|
export const createTestContentEditorExtension = ({ commands = [] } = {}) => {
|
|
|
|
|
const commandMocks = commands.reduce(
|
|
|
|
|
(accum, commandName) => ({
|
|
|
|
|
...accum,
|
|
|
|
|
[commandName]: jest.fn(),
|
|
|
|
|
}),
|
|
|
|
|
{},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
commandMocks,
|
|
|
|
|
tiptapExtension: Node.create({
|
|
|
|
|
name: 'label',
|
|
|
|
|
priority: 101,
|
|
|
|
|
inline: true,
|
|
|
|
|
group: 'inline',
|
|
|
|
|
addCommands() {
|
|
|
|
|
return commands.reduce(
|
|
|
|
|
(accum, commandName) => ({
|
|
|
|
|
...accum,
|
|
|
|
|
[commandName]: (...params) => () => commandMocks[commandName](...params),
|
|
|
|
|
}),
|
|
|
|
|
{},
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
addAttributes() {
|
|
|
|
|
return {
|
|
|
|
|
labelName: {
|
|
|
|
|
default: null,
|
2021-11-11 11:23:49 +05:30
|
|
|
|
parseHTML: (element) => element.dataset.labelName,
|
2021-06-08 01:23:25 +05:30
|
|
|
|
},
|
2021-09-04 01:27:46 +05:30
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
parseHTML() {
|
|
|
|
|
return [
|
|
|
|
|
{
|
|
|
|
|
tag: 'span[data-reference="label"]',
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
},
|
|
|
|
|
renderHTML({ HTMLAttributes }) {
|
|
|
|
|
return ['span', HTMLAttributes, 0];
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
serializer: (state, node) => {
|
|
|
|
|
state.write(`~${node.attrs.labelName}`);
|
|
|
|
|
state.closeBlock(node);
|
2021-06-08 01:23:25 +05:30
|
|
|
|
},
|
2021-09-04 01:27:46 +05:30
|
|
|
|
};
|
|
|
|
|
};
|
2021-12-11 22:18:48 +05:30
|
|
|
|
|
|
|
|
|
export const triggerNodeInputRule = ({ tiptapEditor, inputRuleText }) => {
|
|
|
|
|
const { view } = tiptapEditor;
|
|
|
|
|
const { state } = tiptapEditor;
|
|
|
|
|
const { selection } = state;
|
|
|
|
|
|
|
|
|
|
// Triggers the event handler that input rules listen to
|
|
|
|
|
view.someProp('handleTextInput', (f) => f(view, selection.from, selection.to, inputRuleText));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const triggerMarkInputRule = ({ tiptapEditor, inputRuleText }) => {
|
|
|
|
|
const { view } = tiptapEditor;
|
|
|
|
|
|
2022-04-04 11:22:00 +05:30
|
|
|
|
tiptapEditor.chain().setContent(inputRuleText).setTextSelection(1).run();
|
2021-12-11 22:18:48 +05:30
|
|
|
|
|
|
|
|
|
const { state } = tiptapEditor;
|
|
|
|
|
const { selection } = state;
|
|
|
|
|
|
|
|
|
|
// Triggers the event handler that input rules listen to
|
|
|
|
|
view.someProp('handleTextInput', (f) =>
|
|
|
|
|
f(view, selection.from, inputRuleText.length + 1, inputRuleText),
|
|
|
|
|
);
|
|
|
|
|
};
|
2022-05-07 20:08:51 +05:30
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Executes an action that triggers a transaction in the
|
|
|
|
|
* tiptap Editor. Returns a promise that resolves
|
|
|
|
|
* after the transaction completes
|
|
|
|
|
* @param {*} params.tiptapEditor Tiptap editor
|
|
|
|
|
* @param {*} params.action A function that triggers a transaction in the tiptap Editor
|
|
|
|
|
* @returns A promise that resolves when the transaction completes
|
|
|
|
|
*/
|
2022-07-16 23:28:13 +05:30
|
|
|
|
export const waitUntilNextDocTransaction = ({ tiptapEditor, action = () => {} }) => {
|
2022-05-07 20:08:51 +05:30
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
|
const handleTransaction = () => {
|
|
|
|
|
tiptapEditor.off('update', handleTransaction);
|
|
|
|
|
resolve();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
tiptapEditor.on('update', handleTransaction);
|
|
|
|
|
action();
|
|
|
|
|
});
|
|
|
|
|
};
|
2023-01-13 00:05:48 +05:30
|
|
|
|
|
|
|
|
|
export const createTiptapEditor = (extensions = []) =>
|
|
|
|
|
createTestEditor({
|
|
|
|
|
extensions: [
|
|
|
|
|
Audio,
|
|
|
|
|
Blockquote,
|
|
|
|
|
Bold,
|
|
|
|
|
BulletList,
|
|
|
|
|
Code,
|
|
|
|
|
CodeBlockHighlight,
|
|
|
|
|
DescriptionItem,
|
|
|
|
|
DescriptionList,
|
|
|
|
|
Details,
|
|
|
|
|
DetailsContent,
|
|
|
|
|
Emoji,
|
|
|
|
|
FootnoteDefinition,
|
|
|
|
|
FootnoteReference,
|
|
|
|
|
FootnotesSection,
|
|
|
|
|
Frontmatter,
|
|
|
|
|
Figure,
|
|
|
|
|
FigureCaption,
|
|
|
|
|
HardBreak,
|
|
|
|
|
Heading,
|
|
|
|
|
HorizontalRule,
|
|
|
|
|
...HTMLMarks,
|
|
|
|
|
...HTMLNodes,
|
|
|
|
|
Highlight,
|
|
|
|
|
Image,
|
|
|
|
|
InlineDiff,
|
|
|
|
|
Italic,
|
|
|
|
|
Link,
|
|
|
|
|
ListItem,
|
|
|
|
|
OrderedList,
|
|
|
|
|
ReferenceDefinition,
|
|
|
|
|
Strike,
|
|
|
|
|
Table,
|
|
|
|
|
TableCell,
|
|
|
|
|
TableHeader,
|
|
|
|
|
TableRow,
|
|
|
|
|
TableOfContents,
|
|
|
|
|
TaskItem,
|
|
|
|
|
TaskList,
|
|
|
|
|
Video,
|
|
|
|
|
...extensions,
|
|
|
|
|
],
|
|
|
|
|
});
|