import { describe, expect, it } from 'vitest'; import { Editor } from '@tiptap/core'; import StarterKit from '@tiptap/starter-kit'; import { PlainTextPaste } from '../plain-text-paste'; function makeEditor(content = '
') { return new Editor({ element: document.createElement('div'), extensions: [StarterKit, PlainTextPaste], coreExtensionOptions: { clipboardTextSerializer: { blockSeparator: '\n' } }, content, }); } // jsdom has no ClipboardEvent, which pasteText constructs when given none. function paste(editor: Editor, text: string) { editor.view.pasteText(text, new Event('paste') as ClipboardEvent); } describe('plain-text paste', () => { // A paste that ends in a list leaves the empty paragraph the cursor was in // below it, hence the trailing in those expectations. it('keeps blank lines as empty paragraphs', () => { const editor = makeEditor(); paste(editor, 'Hi Joost,\n\nThanks for the write-up.\n\n\nPHASE 1'); expect(editor.getHTML()).toBe( 'Hi Joost,
Thanks for the write-up.
PHASE 1
', ); editor.destroy(); }); it('treats CRLF and CR line endings like LF', () => { const editor = makeEditor(); paste(editor, 'one\r\n\r\ntwo\rthree'); expect(editor.getHTML()).toBe('one
two
three
'); editor.destroy(); }); it('pastes a single line inline without splitting the paragraph', () => { const editor = makeEditor('Hello world
'); editor.commands.setTextSelection(7); paste(editor, 'big '); expect(editor.getHTML()).toBe('Hello big world
'); editor.destroy(); }); it('turns dash items into a bullet list, dropping the blank lines between them', () => { const editor = makeEditor(); paste(editor, 'How it works:\n\n- One\n\n- Two\n\nAfter'); expect(editor.getHTML()).toBe( 'How it works:
' + 'One
Two
After
', ); editor.destroy(); }); it('accepts * and • as bullet markers', () => { const editor = makeEditor(); paste(editor, '* One\n• Two'); expect(editor.getHTML()).toBe('One
Two
One
Two
Text
' + 'Three
Four
One
Dash
Both drafts:
filenode-14
blobext-01
After
', ); editor.destroy(); }); it('nests indented items', () => { const editor = makeEditor(); paste(editor, '- Outer\n - Inner\n- Next'); expect(editor.getHTML()).toBe( 'Outer
Inner
Next
A long item that the
sender wrapped
Next
Costs - roughly
--
Linus
'); editor.destroy(); }); it('copies back out as the same text', () => { const text = 'Hi Joost,\n\nThanks for the write-up.\n\n\nPHASE 1'; const editor = makeEditor(); paste(editor, text); // TipTap's serializer reads the editor selection, not the slice it gets. editor.commands.selectAll(); const slice = editor.state.selection.content(); const copied = editor.view.someProp('clipboardTextSerializer', (f) => f(slice, editor.view)); expect(copied).toBe(text); editor.destroy(); }); });