一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
110 行
3.5 KiB
TypeScript
110 行
3.5 KiB
TypeScript
import { EditState, insertTextAtSelection, toggleCodeBlock, toggleLinePrefix, toggleWrap, wrapWithLink } from './markdownEditing';
|
|
|
|
const state = (content: string, selectionStart: number, selectionEnd = selectionStart): EditState => {
|
|
return { content, selectionStart, selectionEnd };
|
|
};
|
|
|
|
describe('markdownEditing', () => {
|
|
test('insertTextAtSelection inserts at the cursor and moves it', () => {
|
|
expect(insertTextAtSelection(state('ac', 1), 'b')).toEqual({
|
|
content: 'abc',
|
|
selectionStart: 2,
|
|
selectionEnd: 2,
|
|
});
|
|
});
|
|
|
|
test('insertTextAtSelection replaces the selection', () => {
|
|
expect(insertTextAtSelection(state('aXYb', 1, 3), 'Z')).toEqual({
|
|
content: 'aZb',
|
|
selectionStart: 2,
|
|
selectionEnd: 2,
|
|
});
|
|
});
|
|
|
|
test('toggleWrap wraps and unwraps a selection', () => {
|
|
const wrapped = toggleWrap(state('hello', 0, 5), '**', 'strong text');
|
|
expect(wrapped).toEqual({ content: '**hello**', selectionStart: 2, selectionEnd: 7 });
|
|
|
|
expect(toggleWrap(wrapped, '**', 'strong text')).toEqual({
|
|
content: 'hello',
|
|
selectionStart: 0,
|
|
selectionEnd: 5,
|
|
});
|
|
});
|
|
|
|
test('toggleWrap unwraps markers included in the selection', () => {
|
|
expect(toggleWrap(state('**hello**', 0, 9), '**', 'strong text')).toEqual({
|
|
content: 'hello',
|
|
selectionStart: 0,
|
|
selectionEnd: 5,
|
|
});
|
|
});
|
|
|
|
test('toggleWrap inserts the placeholder when there is no selection', () => {
|
|
expect(toggleWrap(state('', 0), '_', 'italic text')).toEqual({
|
|
content: '_italic text_',
|
|
selectionStart: 1,
|
|
selectionEnd: 12,
|
|
});
|
|
});
|
|
|
|
test('toggleLinePrefix adds and removes a prefix on the selected lines', () => {
|
|
const prefixed = toggleLinePrefix(state('one\ntwo', 0, 7), { add: '- ', detect: /^[-*+] / });
|
|
expect(prefixed.content).toBe('- one\n- two');
|
|
|
|
expect(toggleLinePrefix(prefixed, { add: '- ', detect: /^[-*+] / }).content).toBe('one\ntwo');
|
|
});
|
|
|
|
test('toggleLinePrefix skips empty lines and keeps the cursor after the prefix', () => {
|
|
const result = toggleLinePrefix(state('one\n\ntwo', 0, 8), { add: '- ', detect: /^[-*+] / });
|
|
expect(result.content).toBe('- one\n\n- two');
|
|
|
|
const cursor = toggleLinePrefix(state('one', 0), { add: '# ', detect: /^#{1,6} / });
|
|
expect(cursor).toEqual({ content: '# one', selectionStart: 2, selectionEnd: 2 });
|
|
});
|
|
|
|
test('toggleLinePrefix only affects the line under the cursor', () => {
|
|
expect(toggleLinePrefix(state('one\ntwo', 4), { add: '> ', detect: /^> ?/ }).content).toBe('one\n> two');
|
|
});
|
|
|
|
test('toggleLinePrefix shifts a selection that starts mid-line', () => {
|
|
expect(toggleLinePrefix(state('one', 1, 3), { add: '- ', detect: /^[-*+] / })).toEqual({
|
|
content: '- one',
|
|
selectionStart: 3,
|
|
selectionEnd: 5,
|
|
});
|
|
});
|
|
|
|
test('toggleCodeBlock wraps a selection in a fenced block', () => {
|
|
expect(toggleCodeBlock(state('code', 0, 4))).toEqual({
|
|
content: '```\ncode\n```',
|
|
selectionStart: 4,
|
|
selectionEnd: 8,
|
|
});
|
|
});
|
|
|
|
test('toggleCodeBlock inserts empty fences with the cursor inside', () => {
|
|
expect(toggleCodeBlock(state('', 0))).toEqual({
|
|
content: '```\n\n```',
|
|
selectionStart: 4,
|
|
selectionEnd: 4,
|
|
});
|
|
});
|
|
|
|
test('wrapWithLink wraps the selection and keeps it selected', () => {
|
|
expect(wrapWithLink(state('hello', 0, 5), 'https://example.com')).toEqual({
|
|
content: '[hello](https://example.com)',
|
|
selectionStart: 1,
|
|
selectionEnd: 6,
|
|
});
|
|
});
|
|
|
|
test('wrapWithLink places the cursor inside the brackets when empty', () => {
|
|
expect(wrapWithLink(state('', 0), 'https://example.com')).toEqual({
|
|
content: '[](https://example.com)',
|
|
selectionStart: 1,
|
|
selectionEnd: 1,
|
|
});
|
|
});
|
|
});
|