import { Clipboard, Waiter } from '@ephox/agar'; import { context, describe, it } from '@ephox/bedrock-client'; import { TinyAssertions, TinyDom, TinyHooks, TinySelections } from '@ephox/wrap-mcagar'; import { assert } from 'chai'; import Editor from 'tinymce/core/api/Editor'; import { PastePostProcessEvent, PastePreProcessEvent } from 'tinymce/core/api/EventTypes'; import { EditorEvent } from 'tinymce/core/api/util/EventDispatcher'; import * as InternalHtml from 'tinymce/core/paste/InternalHtml'; import TablePlugin from 'tinymce/plugins/table/Plugin'; describe('browser.tinymce.core.paste.InternalClipboardTest', () => { let dataTransfer: DataTransfer | undefined; let lastPreProcessEvent: EditorEvent | undefined; let lastPostProcessEvent: EditorEvent | undefined; const hook = TinyHooks.bddSetupLight({ plugins: 'table', init_instance_callback: (editor: Editor) => { editor.on('PastePreProcess', (evt) => { lastPreProcessEvent = evt; }); editor.on('PastePostProcess', (evt) => { lastPostProcessEvent = evt; }); editor.on('copy cut paste', (e) => { dataTransfer = e.clipboardData; }); }, base_url: '/project/tinymce/js/tinymce' }, [ TablePlugin ]); const resetProcessEvents = () => { lastPreProcessEvent = undefined; lastPostProcessEvent = undefined; }; const cutCopyDataTransferEvent = (editor: Editor, type: 'cut' | 'copy') => { const action = type === 'cut' ? Clipboard.cut : Clipboard.copy; action(TinyDom.body(editor)); }; const pasteDataTransferEvent = (editor: Editor, data: Record) => Clipboard.pasteItems(TinyDom.body(editor), data); const assertClipboardData = (expectedHtml: string, expectedText: string) => { assert.equal(dataTransfer?.getData('text/html'), expectedHtml, 'text/html data should match'); assert.equal(dataTransfer?.getData('text/plain'), expectedText, 'text/plain data should match'); }; const copy = (editor: Editor, html: string, spath: number[], soffset: number, fpath: number[], foffset: number) => { editor.setContent(html); TinySelections.setSelection(editor, spath, soffset, fpath, foffset); cutCopyDataTransferEvent(editor, 'copy'); }; const cut = (editor: Editor, html: string, spath: number[], soffset: number, fpath: number[], foffset: number) => { editor.setContent(html); TinySelections.setSelection(editor, spath, soffset, fpath, foffset); cutCopyDataTransferEvent(editor, 'cut'); }; const paste = (editor: Editor, startHtml: string, pasteData: Record, spath: number[], soffset: number, fpath: number[], foffset: number) => { editor.setContent(startHtml); TinySelections.setSelection(editor, spath, soffset, fpath, foffset); resetProcessEvents(); pasteDataTransferEvent(editor, pasteData); }; context('copy', () => { it('TBA: Copy simple text', () => { const editor = hook.editor(); copy(editor, '

text

', [ 0, 0 ], 0, [ 0, 0 ], 4); assertClipboardData('text', 'text'); TinyAssertions.assertContent(editor, '

text

'); TinyAssertions.assertSelection(editor, [ 0, 0 ], 0, [ 0, 0 ], 4); }); it('TBA: Copy inline elements', () => { const editor = hook.editor(); copy(editor, '

text

', [ 0, 0 ], 0, [ 0, 2 ], 1); assertClipboardData('text', 'text'); TinyAssertions.assertContent(editor, '

text

'); TinyAssertions.assertSelection(editor, [ 0, 0 ], 0, [ 0, 2 ], 1); }); it('TBA: Copy partially selected inline elements', () => { const editor = hook.editor(); copy(editor, '

acde

', [ 0, 0 ], 0, [ 0, 1, 0 ], 1); assertClipboardData('ac', 'ac'); TinyAssertions.assertContent(editor, '

acde

'); TinyAssertions.assertSelection(editor, [ 0, 0 ], 0, [ 0, 1, 0 ], 1); }); it('TBA: Copy collapsed selection', () => { const editor = hook.editor(); copy(editor, '

abc

', [ 0, 0 ], 1, [ 0, 0 ], 1); assertClipboardData('', ''); TinyAssertions.assertContent(editor, '

abc

'); TinyAssertions.assertSelection(editor, [ 0, 0 ], 1, [ 0, 0 ], 1); }); it('TBA: Copy collapsed selection with table selection', () => { const editor = hook.editor(); copy(editor, '' + '' + '' + '' + '' + '' + '' + '
ab
', [ 0, 0, 0, 1, 0 ], 0, [ 0, 0, 0, 1, 0 ], 0); assertClipboardData( '\n' + '\n' + '\n' + '\n' + '\n' + '\n' + '\n' + '
ab
', 'ab'); TinyAssertions.assertSelection(editor, [ 0, 0, 0, 1, 0 ], 0, [ 0, 0, 0, 1, 0 ], 0); }); it('TINY-8563: Copy cef element', () => { const editor = hook.editor(); copy(editor, '

abc

', [ 0 ], 1, [ 0 ], 2); assertClipboardData('bc', 'bc'); TinyAssertions.assertContent(editor, '

abc

'); TinyAssertions.assertSelection(editor, [ 0 ], 1, [ 0 ], 2); }); }); context('cut', () => { const pWaitUntilAssertContent = (editor: Editor, expected: string) => Waiter.pTryUntil('Cut is async now, so need to wait for content', () => TinyAssertions.assertContent(editor, expected)); it('TBA: Cut simple text', async () => { const editor = hook.editor(); cut(editor, '

text

', [ 0, 0 ], 0, [ 0, 0 ], 4); assertClipboardData('text', 'text'); await pWaitUntilAssertContent(editor, ''); TinyAssertions.assertSelection(editor, [ 0 ], 0, [ 0 ], 0); }); it('TBA: Cut inline elements', async () => { const editor = hook.editor(); cut(editor, '

text

', [ 0, 0 ], 0, [ 0, 2 ], 1); assertClipboardData('text', 'text'); await pWaitUntilAssertContent(editor, ''); TinyAssertions.assertSelection(editor, [ 0 ], 0, [ 0 ], 0); }); it('TBA: Cut partially selected inline elements', async () => { const editor = hook.editor(); cut(editor, '

acde

', [ 0, 0 ], 0, [ 0, 1, 0 ], 1); assertClipboardData('ac', 'ac'); await pWaitUntilAssertContent(editor, '

de

'); TinyAssertions.assertSelection(editor, [ 0, 0, 0 ], 0, [ 0, 0, 0 ], 0); }); it('TBA: Cut collapsed selection', async () => { const editor = hook.editor(); cut(editor, '

abc

', [ 0, 0 ], 1, [ 0, 0 ], 1); assertClipboardData('', ''); await pWaitUntilAssertContent(editor, '

abc

'); TinyAssertions.assertSelection(editor, [ 0, 0 ], 1, [ 0, 0 ], 1); }); it('TINY-8563: Cut cef element', async () => { const editor = hook.editor(); cut(editor, '

abc

', [ 0 ], 1, [ 0 ], 2); assertClipboardData('bc', 'bc'); await pWaitUntilAssertContent(editor, '

a

'); TinyAssertions.assertSelection(editor, [ 0, 0 ], 1, [ 0, 0 ], 1); }); }); context('paste', () => { const assertLastPreProcessEvent = (expectedData: { internal: boolean; content: string }) => { assert.equal(lastPreProcessEvent?.internal, expectedData.internal, 'Internal property should be equal'); assert.equal(lastPreProcessEvent?.content, expectedData.content, 'Content property should be equal'); }; const assertLastPostProcessEvent = (expectedData: { internal: boolean; content: string }) => { assert.equal(lastPostProcessEvent?.internal, expectedData.internal, 'Internal property should be equal'); assert.equal(lastPostProcessEvent?.node.innerHTML, expectedData.content, 'Content property should be equal'); }; const pWaitForProcessEvents = () => Waiter.pTryUntil('Did not get any events fired', () => { assert.isDefined(lastPreProcessEvent, 'PastePreProcess event object'); assert.isDefined(lastPostProcessEvent, 'PastePostProcess event object'); }); it('TBA: Paste external content', async () => { const editor = hook.editor(); paste(editor, '

abc

', { 'text/plain': 'X', 'text/html': '

X

' }, [ 0, 0 ], 0, [ 0, 0 ], 3); await pWaitForProcessEvents(); assertLastPreProcessEvent({ internal: false, content: 'X' }); assertLastPostProcessEvent({ internal: false, content: 'X' }); }); it('TBA: Paste external content treated as plain text', async () => { const editor = hook.editor(); paste(editor, '

abc

', { 'text/html': '

X

' }, [ 0, 0 ], 0, [ 0, 0 ], 3); await pWaitForProcessEvents(); assertLastPreProcessEvent({ internal: false, content: 'X' }); assertLastPostProcessEvent({ internal: false, content: 'X' }); }); it('TBA: Paste internal content with mark', async () => { const editor = hook.editor(); paste(editor, '

abc

', { 'text/plain': 'X', 'text/html': InternalHtml.mark('

X

') }, [ 0, 0 ], 0, [ 0, 0 ], 3); await pWaitForProcessEvents(); assertLastPreProcessEvent({ internal: true, content: '

X

' }); assertLastPostProcessEvent({ internal: true, content: '

X

' }); }); it('TBA: Paste internal content with mime', async () => { const editor = hook.editor(); paste(editor, '

abc

', { 'text/plain': 'X', 'text/html': '

X

', 'x-tinymce/html': '

X

' }, [ 0, 0 ], 0, [ 0, 0 ], 3); await pWaitForProcessEvents(); assertLastPreProcessEvent({ internal: true, content: '

X

' }); assertLastPostProcessEvent({ internal: true, content: '

X

' }); }); }); });