2022-08-08 17:00:14 +02:00
|
|
|
/**
|
|
|
|
* @jest-environment jsdom
|
|
|
|
*/
|
|
|
|
|
|
|
|
import { EditorSelection, EditorState, SelectionRange } from '@codemirror/state';
|
|
|
|
import { EditorView } from '@codemirror/view';
|
|
|
|
import {
|
|
|
|
toggleBolded, toggleCode, toggleHeaderLevel, toggleItalicized, toggleMath, updateLink,
|
|
|
|
} from './markdownCommands';
|
|
|
|
import { GFM as GithubFlavoredMarkdownExt } from '@lezer/markdown';
|
|
|
|
import { markdown } from '@codemirror/lang-markdown';
|
|
|
|
import { MarkdownMathExtension } from './markdownMathParser';
|
|
|
|
import { indentUnit } from '@codemirror/language';
|
|
|
|
|
|
|
|
// Creates and returns a minimal editor with markdown extensions
|
|
|
|
const createEditor = (initialText: string, initialSelection: SelectionRange): EditorView => {
|
|
|
|
return new EditorView({
|
|
|
|
doc: initialText,
|
|
|
|
selection: EditorSelection.create([initialSelection]),
|
|
|
|
extensions: [
|
|
|
|
markdown({
|
|
|
|
extensions: [MarkdownMathExtension, GithubFlavoredMarkdownExt],
|
|
|
|
}),
|
|
|
|
indentUnit.of('\t'),
|
|
|
|
EditorState.tabSize.of(4),
|
|
|
|
],
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
describe('markdownCommands', () => {
|
|
|
|
it('should bold/italicize everything selected', () => {
|
|
|
|
const initialDocText = 'Testing...';
|
|
|
|
const editor = createEditor(
|
|
|
|
initialDocText, EditorSelection.range(0, initialDocText.length)
|
|
|
|
);
|
|
|
|
|
|
|
|
toggleBolded(editor);
|
|
|
|
|
|
|
|
let mainSel = editor.state.selection.main;
|
|
|
|
const boldedText = '**Testing...**';
|
|
|
|
expect(editor.state.doc.toString()).toBe(boldedText);
|
|
|
|
expect(mainSel.from).toBe(0);
|
|
|
|
expect(mainSel.to).toBe(boldedText.length);
|
|
|
|
|
|
|
|
toggleBolded(editor);
|
|
|
|
mainSel = editor.state.selection.main;
|
|
|
|
expect(editor.state.doc.toString()).toBe(initialDocText);
|
|
|
|
expect(mainSel.from).toBe(0);
|
|
|
|
expect(mainSel.to).toBe(initialDocText.length);
|
|
|
|
|
|
|
|
toggleItalicized(editor);
|
|
|
|
expect(editor.state.doc.toString()).toBe('*Testing...*');
|
|
|
|
|
|
|
|
toggleItalicized(editor);
|
|
|
|
expect(editor.state.doc.toString()).toBe('Testing...');
|
|
|
|
});
|
|
|
|
|
2022-09-05 13:50:32 +02:00
|
|
|
it('for a cursor, bolding, then italicizing, should produce a bold-italic region', () => {
|
|
|
|
const initialDocText = '';
|
|
|
|
const editor = createEditor(
|
|
|
|
initialDocText, EditorSelection.cursor(0)
|
|
|
|
);
|
|
|
|
|
|
|
|
toggleBolded(editor);
|
|
|
|
toggleItalicized(editor);
|
|
|
|
expect(editor.state.doc.toString()).toBe('******');
|
|
|
|
|
|
|
|
editor.dispatch(editor.state.replaceSelection('Test'));
|
|
|
|
expect(editor.state.doc.toString()).toBe('***Test***');
|
|
|
|
|
|
|
|
toggleItalicized(editor);
|
|
|
|
editor.dispatch(editor.state.replaceSelection(' Test'));
|
|
|
|
expect(editor.state.doc.toString()).toBe('***Test*** Test');
|
|
|
|
});
|
|
|
|
|
2022-08-08 17:00:14 +02:00
|
|
|
it('toggling math should both create and navigate out of math regions', () => {
|
|
|
|
const initialDocText = 'Testing... ';
|
|
|
|
const editor = createEditor(initialDocText, EditorSelection.cursor(initialDocText.length));
|
|
|
|
|
|
|
|
toggleMath(editor);
|
|
|
|
expect(editor.state.doc.toString()).toBe('Testing... $$');
|
|
|
|
expect(editor.state.selection.main.empty).toBe(true);
|
|
|
|
|
|
|
|
editor.dispatch(editor.state.replaceSelection('3 + 3 \\neq 5'));
|
|
|
|
expect(editor.state.doc.toString()).toBe('Testing... $3 + 3 \\neq 5$');
|
|
|
|
|
|
|
|
toggleMath(editor);
|
|
|
|
editor.dispatch(editor.state.replaceSelection('...'));
|
|
|
|
expect(editor.state.doc.toString()).toBe('Testing... $3 + 3 \\neq 5$...');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('toggling inline code should both create and navigate out of an inline code region', () => {
|
|
|
|
const initialDocText = 'Testing...\n\n';
|
|
|
|
const editor = createEditor(initialDocText, EditorSelection.cursor(initialDocText.length));
|
|
|
|
|
|
|
|
toggleCode(editor);
|
|
|
|
editor.dispatch(editor.state.replaceSelection('f(x) = ...'));
|
|
|
|
toggleCode(editor);
|
|
|
|
|
|
|
|
editor.dispatch(editor.state.replaceSelection(' is a function.'));
|
|
|
|
expect(editor.state.doc.toString()).toBe('Testing...\n\n`f(x) = ...` is a function.');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should set headers to the proper levels (when toggling)', () => {
|
|
|
|
const initialDocText = 'Testing...\nThis is a test.';
|
|
|
|
const editor = createEditor(initialDocText, EditorSelection.cursor(3));
|
|
|
|
|
|
|
|
toggleHeaderLevel(1)(editor);
|
|
|
|
|
|
|
|
let mainSel = editor.state.selection.main;
|
|
|
|
expect(editor.state.doc.toString()).toBe('# Testing...\nThis is a test.');
|
|
|
|
expect(mainSel.empty).toBe(true);
|
|
|
|
expect(mainSel.from).toBe('# Testing...'.length);
|
|
|
|
|
|
|
|
toggleHeaderLevel(2)(editor);
|
|
|
|
|
|
|
|
mainSel = editor.state.selection.main;
|
|
|
|
expect(editor.state.doc.toString()).toBe('## Testing...\nThis is a test.');
|
|
|
|
expect(mainSel.empty).toBe(true);
|
|
|
|
expect(mainSel.from).toBe('## Testing...'.length);
|
|
|
|
|
|
|
|
toggleHeaderLevel(2)(editor);
|
|
|
|
|
|
|
|
mainSel = editor.state.selection.main;
|
|
|
|
expect(editor.state.doc.toString()).toEqual(initialDocText);
|
|
|
|
expect(mainSel.empty).toBe(true);
|
|
|
|
expect(mainSel.from).toBe('Testing...'.length);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('headers should toggle properly within block quotes', () => {
|
|
|
|
const initialDocText = 'Testing...\n\n> This is a test.\n> ...a test';
|
|
|
|
const editor = createEditor(
|
|
|
|
initialDocText,
|
|
|
|
EditorSelection.cursor('Testing...\n\n> This'.length)
|
|
|
|
);
|
|
|
|
|
|
|
|
toggleHeaderLevel(1)(editor);
|
|
|
|
|
|
|
|
const mainSel = editor.state.selection.main;
|
|
|
|
expect(editor.state.doc.toString()).toBe(
|
|
|
|
'Testing...\n\n> # This is a test.\n> ...a test'
|
|
|
|
);
|
|
|
|
expect(mainSel.empty).toBe(true);
|
|
|
|
expect(mainSel.from).toBe('Testing...\n\n> # This is a test.'.length);
|
|
|
|
|
|
|
|
toggleHeaderLevel(3)(editor);
|
|
|
|
|
|
|
|
expect(editor.state.doc.toString()).toBe(
|
|
|
|
'Testing...\n\n> ### This is a test.\n> ...a test'
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2022-11-21 13:39:52 +02:00
|
|
|
// We need to disable this test because it randomly fails on CI.
|
|
|
|
//
|
|
|
|
// ● markdownCommands › block math should properly toggle within block quotes
|
|
|
|
//
|
|
|
|
// expect(received).toEqual(expected) // deep equality
|
|
|
|
//
|
|
|
|
// - Expected - 1
|
|
|
|
// + Received + 3
|
|
|
|
//
|
|
|
|
// Testing...
|
|
|
|
//
|
|
|
|
// - > This is a test.
|
|
|
|
// + > $$
|
|
|
|
// + > This is$$ a test.
|
|
|
|
// > y = mx + b
|
|
|
|
// + > $$
|
|
|
|
// > ...a test
|
|
|
|
//
|
|
|
|
// 179 | toggleMath(editor);
|
|
|
|
// 180 | mainSel = editor.state.selection.main;
|
|
|
|
// > 181 | expect(editor.state.doc.toString()).toEqual(initialDocText);
|
|
|
|
// | ^
|
|
|
|
// 182 | expect(mainSel.from).toBe('Testing...\n\n'.length);
|
|
|
|
// 183 | expect(mainSel.to).toBe('Testing...\n\n> This is a test.\n> y = mx + b'.length);
|
|
|
|
// 184 | });
|
|
|
|
|
|
|
|
|
|
|
|
// it('block math should properly toggle within block quotes', () => {
|
|
|
|
// const initialDocText = 'Testing...\n\n> This is a test.\n> y = mx + b\n> ...a test';
|
|
|
|
// const editor = createEditor(
|
|
|
|
// initialDocText,
|
|
|
|
// EditorSelection.range(
|
|
|
|
// 'Testing...\n\n> This'.length,
|
|
|
|
// 'Testing...\n\n> This is a test.\n> y = mx + b'.length
|
|
|
|
// )
|
|
|
|
// );
|
|
|
|
|
|
|
|
// toggleMath(editor);
|
|
|
|
|
|
|
|
// // Toggling math should surround the content in '$$'s
|
|
|
|
// let mainSel = editor.state.selection.main;
|
|
|
|
// expect(editor.state.doc.toString()).toEqual(
|
|
|
|
// 'Testing...\n\n> $$\n> This is a test.\n> y = mx + b\n> $$\n> ...a test'
|
|
|
|
// );
|
|
|
|
// expect(mainSel.from).toBe('Testing...\n\n'.length);
|
|
|
|
// expect(mainSel.to).toBe('Testing...\n\n> $$\n> This is a test.\n> y = mx + b\n> $$'.length);
|
|
|
|
|
|
|
|
// // Change to a cursor --- test cursor expansion
|
|
|
|
// editor.dispatch({
|
|
|
|
// selection: EditorSelection.cursor('Testing...\n\n> $$\n> This is'.length),
|
|
|
|
// });
|
|
|
|
|
|
|
|
// // Toggling math again should remove the '$$'s
|
|
|
|
// toggleMath(editor);
|
|
|
|
// mainSel = editor.state.selection.main;
|
|
|
|
// expect(editor.state.doc.toString()).toEqual(initialDocText);
|
|
|
|
// expect(mainSel.from).toBe('Testing...\n\n'.length);
|
|
|
|
// expect(mainSel.to).toBe('Testing...\n\n> This is a test.\n> y = mx + b'.length);
|
|
|
|
// });
|
2022-08-08 17:00:14 +02:00
|
|
|
|
|
|
|
it('updateLink should replace link titles and isolate URLs if no title is given', () => {
|
|
|
|
const initialDocText = '[foo](http://example.com/)';
|
|
|
|
const editor = createEditor(initialDocText, EditorSelection.cursor('[f'.length));
|
|
|
|
|
|
|
|
updateLink('bar', 'https://example.com/')(editor);
|
|
|
|
expect(editor.state.doc.toString()).toBe(
|
|
|
|
'[bar](https://example.com/)'
|
|
|
|
);
|
|
|
|
|
|
|
|
updateLink('', 'https://example.com/')(editor);
|
|
|
|
expect(editor.state.doc.toString()).toBe(
|
|
|
|
'https://example.com/'
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('toggling math twice, starting on a line with content, should a math block', () => {
|
|
|
|
const initialDocText = 'Testing... ';
|
|
|
|
const editor = createEditor(initialDocText, EditorSelection.cursor(initialDocText.length));
|
|
|
|
|
|
|
|
toggleMath(editor);
|
|
|
|
toggleMath(editor);
|
|
|
|
editor.dispatch(editor.state.replaceSelection('f(x) = ...'));
|
|
|
|
expect(editor.state.doc.toString()).toBe('Testing... \n$$\nf(x) = ...\n$$');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('toggling math twice on an empty line should create an empty math block', () => {
|
|
|
|
const initialDocText = 'Testing...\n\n';
|
|
|
|
const editor = createEditor(initialDocText, EditorSelection.cursor(initialDocText.length));
|
|
|
|
|
|
|
|
toggleMath(editor);
|
|
|
|
toggleMath(editor);
|
|
|
|
editor.dispatch(editor.state.replaceSelection('f(x) = ...'));
|
|
|
|
expect(editor.state.doc.toString()).toBe('Testing...\n\n$$\nf(x) = ...\n$$');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('toggling code twice on an empty line should create an empty code block', () => {
|
|
|
|
const initialDocText = 'Testing...\n\n';
|
|
|
|
const editor = createEditor(initialDocText, EditorSelection.cursor(initialDocText.length));
|
|
|
|
|
|
|
|
// Toggling code twice should create a block code region
|
|
|
|
toggleCode(editor);
|
|
|
|
toggleCode(editor);
|
|
|
|
editor.dispatch(editor.state.replaceSelection('f(x) = ...'));
|
|
|
|
expect(editor.state.doc.toString()).toBe('Testing...\n\n```\nf(x) = ...\n```');
|
|
|
|
|
|
|
|
toggleCode(editor);
|
|
|
|
expect(editor.state.doc.toString()).toBe('Testing...\n\nf(x) = ...\n');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('toggling math twice inside a block quote should produce an empty math block', () => {
|
|
|
|
const initialDocText = '> Testing...> \n> ';
|
|
|
|
const editor = createEditor(initialDocText, EditorSelection.cursor(initialDocText.length));
|
|
|
|
|
|
|
|
toggleMath(editor);
|
|
|
|
toggleMath(editor);
|
|
|
|
editor.dispatch(editor.state.replaceSelection('f(x) = ...'));
|
|
|
|
expect(editor.state.doc.toString()).toBe(
|
|
|
|
'> Testing...> \n> \n> $$\n> f(x) = ...\n> $$'
|
|
|
|
);
|
|
|
|
|
|
|
|
// If we toggle math again, everything from the start of the line with the first
|
|
|
|
// $$ to the end of the document should be selected.
|
|
|
|
toggleMath(editor);
|
|
|
|
const sel = editor.state.selection.main;
|
|
|
|
expect(sel.from).toBe('> Testing...> \n> \n'.length);
|
|
|
|
expect(sel.to).toBe(editor.state.doc.length);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('toggling inline code should both create and navigate out of an inline code region', () => {
|
|
|
|
const initialDocText = 'Testing...\n\n';
|
|
|
|
const editor = createEditor(initialDocText, EditorSelection.cursor(initialDocText.length));
|
|
|
|
|
|
|
|
toggleCode(editor);
|
|
|
|
editor.dispatch(editor.state.replaceSelection('f(x) = ...'));
|
|
|
|
toggleCode(editor);
|
|
|
|
|
|
|
|
editor.dispatch(editor.state.replaceSelection(' is a function.'));
|
|
|
|
expect(editor.state.doc.toString()).toBe('Testing...\n\n`f(x) = ...` is a function.');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|