This commit is contained in:
2025-10-22 15:39:40 +08:00
commit b0b510fac1
2720 changed files with 415933 additions and 0 deletions

View File

@@ -0,0 +1,150 @@
import { RawAssertions } from '@ephox/agar';
import FragmentParser from 'tinymce/plugins/paste/core/FragmentParser';
import { UnitTest } from '@ephox/bedrock';
UnitTest.test('atomic.tinymce.plugins.paste.FragmentParserTest', function () {
const testGetFragmentInfo = function () {
RawAssertions.assertEq(
'Should be the input string and context body',
{
html: 'abc',
context: 'body'
},
FragmentParser.getFragmentInfo('abc')
);
RawAssertions.assertEq(
'Should be the input string without fragment markers and context body', {
html: 'abc',
context: 'body'
},
FragmentParser.getFragmentInfo('<!-- StartFragment -->abc<!-- EndFragment -->')
);
RawAssertions.assertEq(
'Should be the input string without fragment markers and context body', {
html: 'abc',
context: 'body'
},
FragmentParser.getFragmentInfo('<!--StartFragment-->abc<!--EndFragment-->')
);
RawAssertions.assertEq(
'Should be the input string without fragment markers and contents before/after fragment markers', {
html: 'abc',
context: 'body'
},
FragmentParser.getFragmentInfo('X<!--StartFragment-->abc<!--EndFragment-->Y')
);
RawAssertions.assertEq(
'Should be the input string without fragment markers and contents before/after fragment markers',
{
html: '<B>bold</B><I><B>abc</B>This</I>',
context: 'body'
},
FragmentParser.getFragmentInfo('<!DOCTYPE html><BODY><!-- StartFragment --><B>bold</B><I><B>abc</B>This</I><!-- EndFragment --></BODY></HTML>')
);
RawAssertions.assertEq(
'Should be the input string without fragment markers and contents before/after them but with the ul context',
{
html: '<LI>abc</LI>',
context: 'ul'
},
FragmentParser.getFragmentInfo('<BODY><UL><!--StartFragment--><LI>abc</LI><!--EndFragment--></UL></BODY>')
);
RawAssertions.assertEq(
'Should be the input string without fragment markers and contents before/after them but with the ul context',
{
html: '\n<LI>abc</LI>\n',
context: 'ul'
},
FragmentParser.getFragmentInfo('<BODY>\n<UL>\n<!--StartFragment-->\n<LI>abc</LI>\n<!--EndFragment-->\n</UL>\n</BODY>')
);
RawAssertions.assertEq(
'Should be the input string without fragment markers and contents before/after them but with the p context',
{
html: '<B>abc</B>',
context: 'p'
},
FragmentParser.getFragmentInfo('<BODY><P><!--StartFragment--><B>abc</B><!--EndFragment--></P></BODY>')
);
RawAssertions.assertEq(
'Should be the input string without fragment markers and contents before/after them but with the h1 context',
{
html: '<B>abc</B>',
context: 'h1'
},
FragmentParser.getFragmentInfo('<BODY><H1><!--StartFragment--><B>abc</B><!--EndFragment--></H1></BODY>')
);
};
const testGetFragmentHtml = function () {
RawAssertions.assertEq(
'Should be the input string',
'abc',
FragmentParser.getFragmentHtml('abc')
);
RawAssertions.assertEq(
'Should be the input without fragment markers',
'abc',
FragmentParser.getFragmentHtml('<!-- StartFragment -->abc<!-- EndFragment -->')
);
RawAssertions.assertEq(
'Should be the input string without fragment markers',
'abc',
FragmentParser.getFragmentHtml('<!--StartFragment-->abc<!--EndFragment-->')
);
RawAssertions.assertEq(
'Should be the input string without fragment markers and suffix/prefix contents',
'abc',
FragmentParser.getFragmentHtml('X<!--StartFragment-->abc<!--EndFragment-->Y')
);
RawAssertions.assertEq(
'Should be the input string without fragment markers and suffix/prefix contents',
'<B>bold</B><I><B>abc</B>This</I>',
FragmentParser.getFragmentHtml('<!DOCTYPE html><BODY><!-- StartFragment --><B>bold</B><I><B>abc</B>This</I><!-- EndFragment --></BODY></HTML>')
);
RawAssertions.assertEq(
'Should be the input string without fragment markers and suffix/prefix contents',
'<LI>abc</LI>',
FragmentParser.getFragmentHtml('<BODY><UL><!--StartFragment--><LI>abc</LI><!--EndFragment--></UL></BODY>')
);
RawAssertions.assertEq(
'Should be the input string without fragment markers and suffix/prefix contents',
'\n<LI>abc</LI>\n',
FragmentParser.getFragmentHtml('<BODY>\n<UL>\n<!--StartFragment-->\n<LI>abc</LI>\n<!--EndFragment-->\n</UL>\n</BODY>')
);
RawAssertions.assertEq(
'Should be the input string with body element removed',
'<UL><LI>abc</LI></UL>',
FragmentParser.getFragmentHtml('<!DOCTYPE html><HTML><BODY><UL><LI>abc</LI></UL></BODY></HTML>')
);
RawAssertions.assertEq(
'Should be the input string with body element removed',
'<UL><LI>abc</LI></UL>',
FragmentParser.getFragmentHtml('<BODY CLASS="x"><UL><LI>abc</LI></UL></BODY>')
);
RawAssertions.assertEq(
'Should be the input string with fragments and body element removed',
'<UL><LI>abc</LI></UL>',
FragmentParser.getFragmentHtml('<BODY CLASS="x"><!--StartFragment--><UL><LI>abc</LI></UL><!--EndFragment--></BODY>')
);
};
testGetFragmentInfo();
testGetFragmentHtml();
});

View File

@@ -0,0 +1,28 @@
import InternalHtml from 'tinymce/plugins/paste/core/InternalHtml';
import { UnitTest, assert } from '@ephox/bedrock';
UnitTest.test('atomic.tinymce.plugins.paste.InternalHtmlTest', function () {
const testMark = function () {
assert.eq('<!-- x-tinymce/html -->abc', InternalHtml.mark('abc'));
};
const testUnmark = function () {
assert.eq('abc', InternalHtml.unmark('<!-- x-tinymce/html -->abc'));
assert.eq('abc', InternalHtml.unmark('abc<!-- x-tinymce/html -->'));
};
const testIsMarked = function () {
assert.eq(true, InternalHtml.isMarked('<!-- x-tinymce/html -->abc'));
assert.eq(true, InternalHtml.isMarked('abc<!-- x-tinymce/html -->'));
assert.eq(false, InternalHtml.isMarked('abc'));
};
const testInternalHtmlMime = function () {
assert.eq('x-tinymce/html', InternalHtml.internalHtmlMime());
};
testMark();
testUnmark();
testIsMarked();
testInternalHtmlMime();
});

View File

@@ -0,0 +1,234 @@
import { Pipeline, Step } from '@ephox/agar';
import { UnitTest } from '@ephox/bedrock';
import { Arr, Cell } from '@ephox/katamari';
import { LegacyUnit, TinyLoader } from '@ephox/mcagar';
import { Blob, Uint8Array, Window } from '@ephox/sand';
import Delay from 'tinymce/core/api/util/Delay';
import Promise from 'tinymce/core/api/util/Promise';
import { Clipboard } from 'tinymce/plugins/paste/api/Clipboard';
import Plugin from 'tinymce/plugins/paste/Plugin';
import Theme from 'tinymce/themes/modern/Theme';
UnitTest.asynctest('tinymce.plugins.paste.browser.ImagePasteTest', function () {
const success = arguments[arguments.length - 2];
const failure = arguments[arguments.length - 1];
const suite = LegacyUnit.createSuite();
Plugin();
Theme();
const base64ImgSrc = [
'R0lGODdhZABkAHcAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQECgAAACwAAAAAZABkAIEAAAD78jY/',
'P3SsMjIC/4SPqcvtD6OctNqLs968+w+G4kiW5ommR8C27gvHrxrK9g3TIM7f+tcL5n4doZFFLB6F',
'Sc6SCRFIp9SqVTp6BiPXbjer5XG95Ck47IuWy2e0bLz2tt3DR5w8p7vgd2tej6TW5ycCGMM3aFZo',
'OCOYqFjDuOf4KPAHiPh4qZeZuEnXOfjpFto3ilZ6dxqWGreq1br2+hTLtigZaFcJuYOb67DLC+Qb',
'UIt3i2sshyzZtEFc7JwBLT1NXI2drb3N3e39DR4uPk5ebn6Onq6+zu488A4fLz9P335Aj58fb2+g',
'71/P759AePwADBxY8KDAhAr9MWyY7yFEgPYmRgxokWK7jEYa2XGcJ/HjgJAfSXI0mRGlRZUTWUJ0',
'2RCmQpkHaSLEKPKdzYU4c+78VzCo0KFEixo9ijSp0qVMmzp9CjWq1KlUq1q9eqEAADs='
].join('');
const base64ImgSrc2 = [
'R0lGODlhAQABAPAAAP8REf///yH5BAAAAAAALAAAAAABAAEAAAICRAEAOw=='
].join('');
const sTeardown = function (editor) {
return Step.sync(function () {
delete editor.settings.paste_data_images;
delete editor.settings.images_dataimg_filter;
editor.editorUpload.destroy();
});
};
const appendTeardown = function (editor, steps) {
return Arr.bind(steps, function (step) {
return [step, sTeardown(editor)];
});
};
const base64ToBlob = function (base64, type) {
const buff = Window.atob(base64);
const bytes = Uint8Array(buff.length);
for (let i = 0; i < bytes.length; i++) {
bytes[i] = buff.charCodeAt(i);
}
return Blob([bytes], { type });
};
const noop = function () {
};
const mockEvent = function (type, files) {
let event, transferName;
event = {
type,
preventDefault: noop
};
transferName = type === 'drop' ? 'dataTransfer' : 'clipboardData';
event[transferName] = {
files
};
return event;
};
const setupContent = function (editor) {
editor.setContent('<p>a</p>');
LegacyUnit.setSelection(editor, 'p', 0);
return editor.selection.getRng();
};
const waitFor = function (predicate) {
return new Promise(function (resolve, reject) {
const check = function (time, count) {
if (predicate()) {
resolve();
} else {
if (count === 0) {
reject(new Error('Waited for predicate to be true'));
} else {
Delay.setTimeout(function () {
check(time, count - 1);
}, time);
}
}
};
check(10, 100);
});
};
const waitForSelector = function (editor, selector) {
return waitFor(() => editor.dom.select(selector).length > 0);
};
suite.asyncTest('pasteImages should set unique id in blobcache', function (editor, done, die) {
let rng, event;
const clipboard = Clipboard(editor, Cell('html'));
const hasCachedItem = (name) => !!editor.editorUpload.blobCache.get(name);
editor.settings.paste_data_images = true;
rng = setupContent(editor);
event = mockEvent('paste', [
base64ToBlob(base64ImgSrc, 'image/gif'),
base64ToBlob(base64ImgSrc2, 'image/gif')
]);
clipboard.pasteImageData(event, rng);
waitForSelector(editor, 'img').then(function () {
waitFor((editor) => hasCachedItem('mceclip0') && hasCachedItem('mceclip1')).then(() => {
const cachedBlob1 = editor.editorUpload.blobCache.get('mceclip0');
const cachedBlob2 = editor.editorUpload.blobCache.get('mceclip1');
LegacyUnit.equal(base64ImgSrc, cachedBlob1.base64());
LegacyUnit.equal(base64ImgSrc2, cachedBlob2.base64());
done();
}).catch(die);
}).catch(die);
});
suite.asyncTest('dropImages', function (editor, done, die) {
let rng, event;
const clipboard = Clipboard(editor, Cell('html'));
editor.settings.paste_data_images = true;
rng = setupContent(editor);
event = mockEvent('drop', [
base64ToBlob(base64ImgSrc, 'image/gif')
]);
clipboard.pasteImageData(event, rng);
waitForSelector(editor, 'img').then(function () {
LegacyUnit.equal(editor.getContent(), '<p><img src=\"data:image/gif;base64,' + base64ImgSrc + '" />a</p>');
LegacyUnit.strictEqual(editor.dom.select('img')[0].src.indexOf('blob:'), 0);
done();
}).catch(die);
});
suite.asyncTest('pasteImages', function (editor, done, die) {
let rng, event;
const clipboard = Clipboard(editor, Cell('html'));
editor.settings.paste_data_images = true;
rng = setupContent(editor);
event = mockEvent('paste', [
base64ToBlob(base64ImgSrc, 'image/gif')
]);
clipboard.pasteImageData(event, rng);
waitForSelector(editor, 'img').then(function () {
LegacyUnit.equal(editor.getContent(), '<p><img src=\"data:image/gif;base64,' + base64ImgSrc + '" />a</p>');
LegacyUnit.strictEqual(editor.dom.select('img')[0].src.indexOf('blob:'), 0);
done();
}).catch(die);
});
suite.asyncTest('dropImages - images_dataimg_filter', function (editor, done, die) {
let rng, event;
const clipboard = Clipboard(editor, Cell('html'));
editor.settings.paste_data_images = true;
editor.settings.images_dataimg_filter = function (img) {
LegacyUnit.strictEqual(img.src, 'data:image/gif;base64,' + base64ImgSrc);
return false;
};
rng = setupContent(editor);
event = mockEvent('drop', [
base64ToBlob(base64ImgSrc, 'image/gif')
]);
clipboard.pasteImageData(event, rng);
waitForSelector(editor, 'img').then(function () {
LegacyUnit.equal(editor.getContent(), '<p><img src=\"data:image/gif;base64,' + base64ImgSrc + '" />a</p>');
LegacyUnit.strictEqual(editor.dom.select('img')[0].src.indexOf('data:'), 0);
done();
}).catch(die);
});
suite.asyncTest('pasteImages - images_dataimg_filter', function (editor, done, die) {
let rng, event;
const clipboard = Clipboard(editor, Cell('html'));
editor.settings.paste_data_images = true;
editor.settings.images_dataimg_filter = function (img) {
LegacyUnit.strictEqual(img.src, 'data:image/gif;base64,' + base64ImgSrc);
return false;
};
rng = setupContent(editor);
event = mockEvent('paste', [
base64ToBlob(base64ImgSrc, 'image/gif')
]);
clipboard.pasteImageData(event, rng);
waitForSelector(editor, 'img').then(function () {
LegacyUnit.equal(editor.getContent(), '<p><img src=\"data:image/gif;base64,' + base64ImgSrc + '" />a</p>');
LegacyUnit.strictEqual(editor.dom.select('img')[0].src.indexOf('data:'), 0);
done();
}).catch(die);
});
TinyLoader.setup(function (editor, onSuccess, onFailure) {
Pipeline.async({}, appendTeardown(editor, suite.toSteps(editor)), onSuccess, onFailure);
}, {
add_unload_trigger: false,
disable_nodechange: true,
entities: 'raw',
indent: false,
automatic_uploads: false,
plugins: 'paste',
skin_url: '/project/js/tinymce/skins/lightgray'
}, success, failure);
});

View File

@@ -0,0 +1,240 @@
import { GeneralSteps, Logger, Pipeline, RawAssertions, Step, Waiter } from '@ephox/agar';
import { UnitTest } from '@ephox/bedrock';
import { TinyApis, TinyLoader } from '@ephox/mcagar';
import InternalHtml from 'tinymce/plugins/paste/core/InternalHtml';
import Utils from 'tinymce/plugins/paste/core/Utils';
import PastePlugin from 'tinymce/plugins/paste/Plugin';
import TablePlugin from 'tinymce/plugins/table/Plugin';
import Theme from 'tinymce/themes/modern/Theme';
import MockDataTransfer from '../module/test/MockDataTransfer';
UnitTest.asynctest('browser.tinymce.plugins.paste.InternalClipboardTest', function () {
const success = arguments[arguments.length - 2];
const failure = arguments[arguments.length - 1];
let dataTransfer, lastPreProcessEvent, lastPostProcessEvent;
PastePlugin();
TablePlugin();
Theme();
const sResetProcessEvents = Step.sync(function () {
lastPreProcessEvent = null;
lastPostProcessEvent = null;
});
const sCutCopyDataTransferEvent = function (editor, type) {
return Step.sync(function () {
dataTransfer = MockDataTransfer.create({});
editor.fire(type, { clipboardData: dataTransfer });
});
};
const sPasteDataTransferEvent = function (editor, data) {
return Step.sync(function () {
dataTransfer = MockDataTransfer.create(data);
editor.fire('paste', { clipboardData: dataTransfer });
});
};
const sAssertClipboardData = function (expectedHtml, expectedText) {
return Step.sync(function () {
RawAssertions.assertEq('text/html data should match', expectedHtml, dataTransfer.getData('text/html'));
RawAssertions.assertEq('text/plain data should match', expectedText, dataTransfer.getData('text/plain'));
});
};
const sCopy = function (editor, tinyApis, html, spath, soffset, fpath, foffset) {
return GeneralSteps.sequence([
tinyApis.sSetContent(html),
tinyApis.sSetSelection(spath, soffset, fpath, foffset),
sCutCopyDataTransferEvent(editor, 'copy')
]);
};
const sCut = function (editor, tinyApis, html, spath, soffset, fpath, foffset) {
return GeneralSteps.sequence([
tinyApis.sSetContent(html),
tinyApis.sSetSelection(spath, soffset, fpath, foffset),
sCutCopyDataTransferEvent(editor, 'cut')
]);
};
const sPaste = function (editor, tinyApis, startHtml, pasteData, spath, soffset, fpath, foffset) {
return GeneralSteps.sequence([
tinyApis.sSetContent(startHtml),
tinyApis.sSetSelection(spath, soffset, fpath, foffset),
sResetProcessEvents,
sPasteDataTransferEvent(editor, pasteData)
]);
};
const sTestCopy = function (editor, tinyApis) {
return Logger.t('Copy tests', GeneralSteps.sequence([
Logger.t('Copy simple text', GeneralSteps.sequence([
sCopy(editor, tinyApis, '<p>text</p>', [0, 0], 0, [0, 0], 4),
sAssertClipboardData('text', 'text'),
tinyApis.sAssertContent('<p>text</p>'),
tinyApis.sAssertSelection([0, 0], 0, [0, 0], 4)
])),
Logger.t('Copy inline elements', GeneralSteps.sequence([
sCopy(editor, tinyApis, '<p>te<em>x</em>t</p>', [0, 0], 0, [0, 2], 1),
sAssertClipboardData('te<em>x</em>t', 'text'),
tinyApis.sAssertContent('<p>te<em>x</em>t</p>'),
tinyApis.sAssertSelection([0, 0], 0, [0, 2], 1)
])),
Logger.t('Copy partialy selected inline elements', GeneralSteps.sequence([
sCopy(editor, tinyApis, '<p>a<em>cd</em>e</p>', [0, 0], 0, [0, 1, 0], 1),
sAssertClipboardData('a<em>c</em>', 'ac'),
tinyApis.sAssertContent('<p>a<em>cd</em>e</p>'),
tinyApis.sAssertSelection([0, 0], 0, [0, 1, 0], 1)
])),
Logger.t('Copy collapsed selection', GeneralSteps.sequence([
sCopy(editor, tinyApis, '<p>abc</p>', [0, 0], 1, [0, 0], 1),
sAssertClipboardData('', ''),
tinyApis.sAssertContent('<p>abc</p>'),
tinyApis.sAssertSelection([0, 0], 1, [0, 0], 1)
])),
Logger.t('Copy collapsed selection with table selection', GeneralSteps.sequence([
sCopy(editor, tinyApis,
'<table data-mce-selected="1">' +
'<tbody>' +
'<tr>' +
'<td data-mce-first-selected="1" data-mce-selected="1">a</td>' +
'<td data-mce-last-selected="1" data-mce-selected="1">b</td>' +
'</tr>' +
'</tbody>' +
'</table>',
[0, 0, 0, 1, 0], 0, [0, 0, 0, 1, 0], 0),
sAssertClipboardData(
'<table>\n' +
'<tbody>\n' +
'<tr>\n' +
'<td>a</td>\n' +
'<td>b</td>\n' +
'</tr>\n' +
'</tbody>\n' +
'</table>', 'ab'),
tinyApis.sAssertSelection([0, 0, 0, 1, 0], 0, [0, 0, 0, 1, 0], 0)
]))
]));
};
const sTestCut = function (editor, tinyApis) {
const sWaitUntilAssertContent = function (expected) {
return Waiter.sTryUntil('Cut is async now, so need to wait for content', tinyApis.sAssertContent(expected), 100, 1000);
};
return Logger.t('Cut tests', GeneralSteps.sequence([
Logger.t('Cut simple text', GeneralSteps.sequence([
sCut(editor, tinyApis, '<p>text</p>', [0, 0], 0, [0, 0], 4),
sAssertClipboardData('text', 'text'),
sWaitUntilAssertContent(''),
tinyApis.sAssertSelection([0], 0, [0], 0)
])),
Logger.t('Cut inline elements', GeneralSteps.sequence([
sCut(editor, tinyApis, '<p>te<em>x</em>t</p>', [0, 0], 0, [0, 2], 1),
sAssertClipboardData('te<em>x</em>t', 'text'),
sWaitUntilAssertContent(''),
tinyApis.sAssertSelection([0], 0, [0], 0)
])),
Logger.t('Cut partialy selected inline elements', GeneralSteps.sequence([
sCut(editor, tinyApis, '<p>a<em>cd</em>e</p>', [0, 0], 0, [0, 1, 0], 1),
sAssertClipboardData('a<em>c</em>', 'ac'),
sWaitUntilAssertContent('<p><em>d</em>e</p>'),
tinyApis.sAssertSelection([0, 0, 0], 0, [0, 0, 0], 0)
])),
Logger.t('Cut collapsed selection', GeneralSteps.sequence([
sCut(editor, tinyApis, '<p>abc</p>', [0, 0], 1, [0, 0], 1),
sAssertClipboardData('', ''),
sWaitUntilAssertContent('<p>abc</p>'),
tinyApis.sAssertSelection([0, 0], 1, [0, 0], 1)
]))
]));
};
const sAssertLastPreProcessEvent = function (expectedData) {
return Step.sync(function () {
RawAssertions.assertEq('Internal property should be equal', expectedData.internal, lastPreProcessEvent.internal);
RawAssertions.assertEq('Content property should be equal', expectedData.content, lastPreProcessEvent.content);
});
};
const sAssertLastPostProcessEvent = function (expectedData) {
return Step.sync(function () {
RawAssertions.assertEq('Internal property should be equal', expectedData.internal, lastPostProcessEvent.internal);
RawAssertions.assertEq('Content property should be equal', expectedData.content, lastPostProcessEvent.node.innerHTML);
});
};
const sWaitForProcessEvents = Waiter.sTryUntil('Did not get any events fired', Step.sync(function () {
RawAssertions.assertEq('PastePreProcess event object', lastPreProcessEvent !== null, true);
RawAssertions.assertEq('PastePostProcess event object', lastPostProcessEvent !== null, true);
}), 100, 100);
const sTestPaste = function (editor, tinyApis) {
return Logger.t('Paste tests', GeneralSteps.sequence([
Logger.t('Paste external content', GeneralSteps.sequence([
sPaste(editor, tinyApis, '<p>abc</p>', { 'text/plain': 'X', 'text/html': '<p>X</p>' }, [0, 0], 0, [0, 0], 3),
sWaitForProcessEvents,
sAssertLastPreProcessEvent({ internal: false, content: 'X' }),
sAssertLastPostProcessEvent({ internal: false, content: 'X' })
])),
Logger.t('Paste external content treated as plain text', GeneralSteps.sequence([
sPaste(editor, tinyApis, '<p>abc</p>', { 'text/html': '<p>X</p>' }, [0, 0], 0, [0, 0], 3),
sWaitForProcessEvents,
sAssertLastPreProcessEvent({ internal: false, content: 'X' }),
sAssertLastPostProcessEvent({ internal: false, content: 'X' })
])),
Logger.t('Paste internal content with mark', GeneralSteps.sequence([
sPaste(editor, tinyApis, '<p>abc</p>', { 'text/plain': 'X', 'text/html': InternalHtml.mark('<p>X</p>') }, [0, 0], 0, [0, 0], 3),
sWaitForProcessEvents,
sAssertLastPreProcessEvent({ internal: true, content: '<p>X</p>' }),
sAssertLastPostProcessEvent({ internal: true, content: '<p>X</p>' })
])),
Logger.t('Paste internal content with mime', GeneralSteps.sequence([
sPaste(editor, tinyApis, '<p>abc</p>',
{ 'text/plain': 'X', 'text/html': '<p>X</p>', 'x-tinymce/html': '<p>X</p>' },
[0, 0], 0, [0, 0], 3
),
sWaitForProcessEvents,
sAssertLastPreProcessEvent({ internal: true, content: '<p>X</p>' }),
sAssertLastPostProcessEvent({ internal: true, content: '<p>X</p>' })
]))
]));
};
TinyLoader.setup(function (editor, onSuccess, onFailure) {
const tinyApis = TinyApis(editor);
// Disabled tests on Edge 15 due to broken clipboard API
Pipeline.async({}, Utils.isMsEdge() ? [ ] : [
sTestCopy(editor, tinyApis),
sTestCut(editor, tinyApis),
sTestPaste(editor, tinyApis)
], onSuccess, onFailure);
}, {
plugins: 'paste table',
init_instance_callback (editor) {
editor.on('PastePreProcess', function (evt) {
lastPreProcessEvent = evt;
});
editor.on('PastePostProcess', function (evt) {
lastPostProcessEvent = evt;
});
},
skin_url: '/project/js/tinymce/skins/lightgray'
}, success, failure);
});

View File

@@ -0,0 +1,67 @@
import { Assertions } from '@ephox/agar';
import { UnitTest } from '@ephox/bedrock';
import { Arr } from '@ephox/katamari';
import Newlines from 'tinymce/plugins/paste/core/Newlines';
import PastePlugin from 'tinymce/plugins/paste/Plugin';
import Theme from 'tinymce/themes/modern/Theme';
UnitTest.test('tinymce.plugins.paste.browser.NewlinesTest', function () {
Theme();
PastePlugin();
// testing Newlines.isPlainText()
const textCases = [
{
label: 'Basic Chrome markup (including span-wrapped tab)',
content: '<div><span style="white-space:pre"> </span>a</div><div><br></div><div>b</div>',
isText: true
},
{
label: 'Case shouldn\'t matter',
content: '<DIV>a</DIV><DIV><BR></DIV>',
isText: true
},
{
label: 'Support all BR types',
content: '<br><br />',
isText: true
},
{
label: 'Basic IE markup',
content: '<p>a</p><p><br></p><p>b</p>',
isText: true
},
{
label: 'White-space wrapper (Chrome)',
content: '<div><span style="white-space: pre;"> </span>a</div>',
isText: true
},
{
label: 'White-space wrapper (Chrome) with additional styles',
content: '<div><span style="white-space: pre; color: red;"> </span>a</div>',
isText: false
},
{
label: 'Allowed tag but with attributes qualifies string as not a plain text',
content: '<br data-mce-bogus="all" />',
isText: false
}
];
// only DIV,P,BR and SPAN[style="white-space:pre"] tags are allowed in "plain text" string
Arr.each('a,abbr,address,article,aside,audio,b,bdi,bdo,blockquote,button,cite,code,del,details,dfn,dl,em,embed,fieldset,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,hr,i,ins,label,menu,nav,noscript,object,ol,pre,q,s,script,section,select,small,strong,style,sub,sup,svg,table,textarea,time,u,ul,var,video,wbr'.split(','),
function (tag) {
const content = '<p>a</p><' + tag + '>b</' + tag + '><p>c<br>d</p>';
textCases.push({
label: tag.toUpperCase() + ' tag should qualify content (' + content + ') as not a plain text',
content,
isText: false
});
}
);
Arr.each(textCases, function (c) {
Assertions.assertEq(c.label || 'Asserting: ' + c.content, c.isText, Newlines.isPlainText(c.content));
});
});

View File

@@ -0,0 +1,99 @@
import { Assertions, Chain, Pipeline } from '@ephox/agar';
import { UnitTest } from '@ephox/bedrock';
import { Id, Merger, Obj } from '@ephox/katamari';
import EditorManager from 'tinymce/core/api/EditorManager';
import { PasteBin, getPasteBinParent } from 'tinymce/plugins/paste/core/PasteBin';
import PastePlugin from 'tinymce/plugins/paste/Plugin';
import Theme from 'tinymce/themes/modern/Theme';
import ViewBlock from '../module/test/ViewBlock';
UnitTest.asynctest('tinymce.plugins.paste.browser.PasteBin', function () {
const success = arguments[arguments.length - 2];
const failure = arguments[arguments.length - 1];
Theme();
PastePlugin();
const cases = [
{
label: 'TINY-1162: testing nested paste bins',
content: '<div id="mcepastebin" contenteditable="true" data-mce-bogus="all" data-mce-style="position: absolute; top: 0.40000057220458984px;width: 10px; height: 10px; overflow: hidden; opacity: 0" style="position: absolute; top: 0.40000057220458984px;width: 10px; height: 10px; overflow: hidden; opacity: 0"><div id="mcepastebin" data-mce-bogus="all" data-mce-style="position: absolute; top: 0.40000057220458984px;width: 10px; height: 10px; overflow: hidden; opacity: 0" style="position: absolute; top: 0.40000057220458984px;width: 10px; height: 10px; overflow: hidden; opacity: 0">a</div><div id="mcepastebin" data-mce-bogus="all" data-mce-style="position: absolute; top: 0.40000057220458984px;width: 10px; height: 10px; overflow: hidden; opacity: 0" style="position: absolute; top: 0.40000057220458984px;width: 10px; height: 10px; overflow: hidden; opacity: 0">b</div></div>',
result: '<div>a</div><div>b</div>'
},
{
label: 'TINY-1162: testing adjacent paste bins',
content: '<div id="mcepastebin" contenteditable="true" data-mce-bogus="all" data-mce-style="position: absolute; top: 0.40000057220458984px;width: 10px; height: 10px; overflow: hidden; opacity: 0" style="position: absolute; top: 0.40000057220458984px;width: 10px; height: 10px; overflow: hidden; opacity: 0"><p>a</p><p>b</p></div><div id="mcepastebin" contenteditable="true" data-mce-bogus="all" data-mce-style="position: absolute; top: 0.40000057220458984px;width: 10px; height: 10px; overflow: hidden; opacity: 0" style="position: absolute; top: 0.40000057220458984px;width: 10px; height: 10px; overflow: hidden; opacity: 0"><p>c</p></div>',
result: '<p>a</p><p>b</p><p>c</p>'
}
];
const viewBlock = ViewBlock();
const cCreateEditorFromSettings = function (settings?, html?) {
return Chain.async(function (viewBlock: any, next, die) {
const randomId = Id.generate('tiny');
html = html || '<textarea></textarea>';
viewBlock.update(html);
viewBlock.get().firstChild.id = randomId;
EditorManager.init(Merger.merge(settings || {}, {
selector: '#' + randomId,
add_unload_trigger: false,
indent: false,
plugins: 'paste',
skin_url: '/project/js/tinymce/skins/lightgray',
setup (editor) {
editor.on('SkinLoaded', function () {
setTimeout(function () {
next(editor);
}, 0);
});
}
}));
});
};
const cCreateEditorFromHtml = function (html, settings) {
return cCreateEditorFromSettings(settings, html);
};
const cRemoveEditor = function () {
return Chain.op(function (editor: any) {
editor.remove();
});
};
const cAssertCases = function (cases) {
return Chain.op(function (editor: any) {
const pasteBin = PasteBin(editor);
Obj.each(cases, function (c, i) {
getPasteBinParent(editor).appendChild(editor.dom.createFragment(c.content));
Assertions.assertEq(c.label || 'Asserting paste bin case ' + i, c.result, pasteBin.getHtml());
pasteBin.remove();
});
});
};
viewBlock.attach();
Pipeline.async({}, [
Chain.asStep(viewBlock, [
cCreateEditorFromSettings(),
cAssertCases(cases),
cRemoveEditor()
]),
// TINY-1208/TINY-1209: same cases, but for inline editor
Chain.asStep(viewBlock, [
cCreateEditorFromHtml('<div>some text</div>', { inline: true }),
cAssertCases(cases),
cRemoveEditor()
])
], function () {
viewBlock.detach();
success();
}, failure);
});

View File

@@ -0,0 +1,38 @@
import { GeneralSteps, Logger, Pipeline } from '@ephox/agar';
import { UnitTest } from '@ephox/bedrock';
import { TinyApis, TinyLoader } from '@ephox/mcagar';
import Env from 'tinymce/core/api/Env';
import PastePlugin from 'tinymce/plugins/paste/Plugin';
import ModernTheme from 'tinymce/themes/modern/Theme';
import Paste from '../module/test/Paste';
UnitTest.asynctest('tinymce.plugins.paste.browser.PasteFormatToggleTest', (success, failure) => {
ModernTheme();
PastePlugin();
TinyLoader.setup(function (editor, onSuccess, onFailure) {
const tinyApis = TinyApis(editor);
const steps = Env.webkit ? [
Logger.t('paste plain text',
GeneralSteps.sequence([
tinyApis.sExecCommand('mceTogglePlainTextPaste'),
Paste.sPaste(editor, { 'text/html': '<p><strong>test</strong></p>'}),
tinyApis.sAssertContent('<p>test</p>'),
tinyApis.sSetContent(''),
tinyApis.sExecCommand('mceTogglePlainTextPaste'),
Paste.sPaste(editor, { 'text/html': '<p><strong>test</strong></p>'}),
tinyApis.sAssertContent('<p><strong>test</strong></p>')
])
)
] : [];
Pipeline.async({}, steps, onSuccess, onFailure);
}, {
plugins: 'paste',
toolbar: '',
valid_styles: 'font-family,color',
skin_url: '/project/js/tinymce/skins/lightgray'
}, success, failure);
});

View File

@@ -0,0 +1,56 @@
import { Assertions, Chain, Logger, Pipeline } from '@ephox/agar';
import { UnitTest } from '@ephox/bedrock';
import { Merger } from '@ephox/katamari';
import EditorManager from 'tinymce/core/api/EditorManager';
import Plugin from 'tinymce/plugins/paste/Plugin';
import Theme from 'tinymce/themes/modern/Theme';
import ViewBlock from '../module/test/ViewBlock';
UnitTest.asynctest('tinymce.plugins.paste.browser.PasteSettingsTest', function () {
const success = arguments[arguments.length - 2];
const failure = arguments[arguments.length - 1];
const viewBlock = ViewBlock();
Theme();
Plugin();
const cCreateInlineEditor = function (settings) {
return Chain.async(function (viewBlock: any, next, die) {
viewBlock.update('<div id="inline-tiny"></div>');
EditorManager.init(Merger.merge({
selector: '#inline-tiny',
inline: true,
skin_url: '/project/js/tinymce/skins/lightgray',
setup (editor) {
editor.on('SkinLoaded', function () {
next(editor);
});
}
}, settings));
});
};
const cRemoveEditor = Chain.op(function (editor: any) {
editor.remove();
});
viewBlock.attach();
Pipeline.async({}, [
Logger.t('paste_as_text setting', Chain.asStep(viewBlock, [
cCreateInlineEditor({
paste_as_text: true,
plugins: 'paste'
}),
Chain.op(function (editor) {
Assertions.assertEq('Should be text format', 'text', editor.plugins.paste.clipboard.pasteFormat.get());
}),
cRemoveEditor
]))
], function () {
viewBlock.detach();
success();
}, failure);
});

View File

@@ -0,0 +1,69 @@
import { GeneralSteps, Logger, Pipeline } from '@ephox/agar';
import { UnitTest } from '@ephox/bedrock';
import { TinyApis, TinyLoader } from '@ephox/mcagar';
import Env from 'tinymce/core/api/Env';
import PastePlugin from 'tinymce/plugins/paste/Plugin';
import ModernTheme from 'tinymce/themes/modern/Theme';
import Paste from '../module/test/Paste';
UnitTest.asynctest('Browser Test: .PasteStylesTest', function () {
const success = arguments[arguments.length - 2];
const failure = arguments[arguments.length - 1];
ModernTheme();
PastePlugin();
TinyLoader.setup(function (editor, onSuccess, onFailure) {
const tinyApis = TinyApis(editor);
const steps = Env.webkit ? [
Logger.t('Paste span with encoded style attribute, paste_webkit_styles: font-family',
GeneralSteps.sequence([
tinyApis.sSetSetting('paste_webkit_styles', 'font-family'),
tinyApis.sSetContent('<p>test</p>'),
tinyApis.sSetSelection([0, 0], 0, [0, 0], 4),
Paste.sPaste(editor, { 'text/html': '<span style="font-family: &quot;a b&quot;;color:green;">b</span>' }),
tinyApis.sAssertContent('<p><span style="font-family: \'a b\';">b</span></p>')
])
),
Logger.t('Paste span with encoded style attribute, paste_webkit_styles: all',
GeneralSteps.sequence([
tinyApis.sSetSetting('paste_webkit_styles', 'all'),
tinyApis.sSetContent('<p>test</p>'),
tinyApis.sSetSelection([0, 0], 0, [0, 0], 4),
Paste.sPaste(editor, { 'text/html': '<span style="font-family: &quot;a b&quot;; color: green;">b</span>' }),
tinyApis.sAssertContent('<p><span style="font-family: \'a b\'; color: green;">b</span></p>')
])
),
Logger.t('Paste span with encoded style attribute, paste_webkit_styles: none',
GeneralSteps.sequence([
tinyApis.sSetSetting('paste_webkit_styles', 'none'),
tinyApis.sSetContent('<p>test</p>'),
tinyApis.sSetSelection([0, 0], 0, [0, 0], 4),
Paste.sPaste(editor, { 'text/html': '<span style="font-family: &quot;a b&quot;;">b</span>' }),
tinyApis.sAssertContent('<p>b</p>')
])
),
Logger.t('Paste span with encoded style attribute, paste_remove_styles_if_webkit: false',
GeneralSteps.sequence([
tinyApis.sSetSetting('paste_remove_styles_if_webkit', false),
tinyApis.sSetContent('<p>test</p>'),
tinyApis.sSetSelection([0, 0], 0, [0, 0], 4),
Paste.sPaste(editor, { 'text/html': '<span style="font-family: &quot;a b&quot;;">b</span>' }),
tinyApis.sAssertContent('<p><span style="font-family: \'a b\';">b</span></p>')
])
)
] : [];
Pipeline.async({}, steps, onSuccess, onFailure);
}, {
plugins: 'paste',
toolbar: '',
valid_styles: 'font-family,color',
skin_url: '/project/js/tinymce/skins/lightgray'
}, success, failure);
});

View File

@@ -0,0 +1,879 @@
import { Pipeline, Step } from '@ephox/agar';
import { UnitTest } from '@ephox/bedrock';
import { Arr } from '@ephox/katamari';
import { LegacyUnit, TinyLoader } from '@ephox/mcagar';
import Env from 'tinymce/core/api/Env';
import Utils from 'tinymce/plugins/paste/core/Utils';
import Plugin from 'tinymce/plugins/paste/Plugin';
import Theme from 'tinymce/themes/modern/Theme';
import Strings from '../module/test/Strings';
UnitTest.asynctest('tinymce.plugins.paste.browser.ImagePasteTest', function () {
const success = arguments[arguments.length - 2];
const failure = arguments[arguments.length - 1];
const suite = LegacyUnit.createSuite();
Plugin();
Theme();
/* eslint-disable max-len */
const sTeardown = function (editor) {
return Step.sync(function () {
delete editor.settings.paste_remove_styles_if_webkit;
delete editor.settings.paste_retain_style_properties;
delete editor.settings.paste_enable_default_filters;
delete editor.settings.paste_data_images;
delete editor.settings.paste_webkit_styles;
});
};
const appendTeardown = function (editor, steps) {
return Arr.bind(steps, function (step) {
return [step, sTeardown(editor)];
});
};
const trimContent = function (content) {
return content.replace(/^<p>&nbsp;<\/p>\n?/, '').replace(/\n?<p>&nbsp;<\/p>$/, '');
};
suite.test('Plain text toggle event', function (editor) {
const events = [];
editor.on('PastePlainTextToggle', function (e) {
events.push({ state: e.state });
});
editor.execCommand('mceTogglePlainTextPaste');
LegacyUnit.deepEqual(events, [
{ state: true }
], 'Should be enabled');
editor.execCommand('mceTogglePlainTextPaste');
LegacyUnit.deepEqual(events, [
{ state: true },
{ state: false }
], 'Should be disabled');
editor.execCommand('mceTogglePlainTextPaste');
LegacyUnit.deepEqual(events, [
{ state: true },
{ state: false },
{ state: true }
], 'Should be enabled again');
});
suite.test('Paste simple text content', function (editor) {
const rng = editor.dom.createRng();
editor.setContent('<p>1234</p>');
editor.focus();
rng.setStart(editor.getBody().firstChild.firstChild, 1);
rng.setEnd(editor.getBody().firstChild.firstChild, 3);
editor.selection.setRng(rng);
editor.execCommand('mceInsertClipboardContent', false, { content: 'TEST' });
LegacyUnit.equal(editor.getContent(), '<p>1TEST4</p>');
});
suite.test('Paste text with meta and nbsp', function (editor) {
const rng = editor.dom.createRng();
editor.setContent('<p>1&nbsp;</p>');
editor.focus();
rng.setStart(editor.getBody().firstChild.firstChild, 2);
rng.setEnd(editor.getBody().firstChild.firstChild, 2);
editor.selection.setRng(rng);
editor.execCommand('mceInsertClipboardContent', false, { content: '<meta charset="utf-8">TEST' });
LegacyUnit.equal(editor.getContent(), '<p>1 TEST</p>');
});
suite.test('Paste styled text content', function (editor) {
const rng = editor.dom.createRng();
editor.settings.paste_remove_styles_if_webkit = false;
editor.setContent('<p>1234</p>');
rng.setStart(editor.getBody().firstChild.firstChild, 1);
rng.setEnd(editor.getBody().firstChild.firstChild, 3);
editor.selection.setRng(rng);
editor.execCommand('mceInsertClipboardContent', false, { content: '<strong><em><span style="color: red;">TEST</span></em></strong>' });
LegacyUnit.equal(editor.getContent(), '<p>1<strong><em><span style="color: red;">TEST</span></em></strong>4</p>');
});
suite.test('Paste paragraph in paragraph', function (editor) {
const rng = editor.dom.createRng();
editor.setContent('<p>1234</p>');
rng.setStart(editor.getBody().firstChild.firstChild, 1);
rng.setEnd(editor.getBody().firstChild.firstChild, 3);
editor.selection.setRng(rng);
editor.execCommand('mceInsertClipboardContent', false, { content: '<p>TEST</p>' });
LegacyUnit.equal(editor.getContent(), '<p>1</p><p>TEST</p><p>4</p>');
});
suite.test('Paste paragraphs in complex paragraph', function (editor) {
const rng = editor.dom.createRng();
editor.setContent('<p><strong><em>1234</em></strong></p>');
rng.setStart(editor.dom.select('em,i')[0].firstChild, 1);
rng.setEnd(editor.dom.select('em,i')[0].firstChild, 3);
editor.selection.setRng(rng);
editor.execCommand('mceInsertClipboardContent', false, { content: '<p>TEST 1</p><p>TEST 2</p>' });
LegacyUnit.equal(editor.getContent(), '<p><strong><em>1</em></strong></p><p>TEST 1</p><p>TEST 2</p><p><strong><em>4</em></strong></p>');
});
suite.test('Paste Word fake list', function (editor) {
let rng = editor.dom.createRng();
editor.setContent('<p>1234</p>');
rng.setStart(editor.getBody().firstChild.firstChild, 0);
rng.setEnd(editor.getBody().firstChild.firstChild, 4);
editor.selection.setRng(rng);
editor.execCommand('mceInsertClipboardContent', false, { content: Strings.wordList2 });
LegacyUnit.equal(editor.getContent(), '<ul><li>Item 1</li><li>Item 2</li><li>Item 3</li><li>Item 4</li><li>Item 5</li><li>Item 6</li></ul>');
editor.settings.paste_retain_style_properties = 'border';
rng = editor.dom.createRng();
editor.setContent('<p>1234</p>');
rng.setStart(editor.getBody().firstChild.firstChild, 0);
rng.setEnd(editor.getBody().firstChild.firstChild, 4);
editor.selection.setRng(rng);
editor.execCommand('mceInsertClipboardContent', false, { content: '<p class="ListStyle" style="margin-top:0cm;margin-right:0cm;margin-bottom:3.0pt;margin-left:18.0pt;mso-add-space:auto;text-align:justify;text-indent:-18.0pt;mso-list:l0 level1 lfo1;tab-stops:list 18.0pt"><span lang="DE" style="font-family:Verdana;mso-fareast-font-family:Verdana;mso-bidi-font-family:Verdana;color:black"><span style="mso-list:Ignore">\u25CF<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span></span><span lang="DE" style="font-family:Arial;mso-fareast-font-family:Arial;mso-bidi-font-family:Arial;color:black">Item&nbsp; Spaces.<o:p></o:p></span></p>' });
LegacyUnit.equal(editor.getContent(), '<ul><li>Item&nbsp; Spaces.</li></ul>');
rng = editor.dom.createRng();
editor.setContent('<p>1234</p>');
rng.setStart(editor.getBody().firstChild.firstChild, 0);
rng.setEnd(editor.getBody().firstChild.firstChild, 4);
editor.selection.setRng(rng);
editor.execCommand('mceInsertClipboardContent', false, { content: '<p class="ListStyle" style="margin-left:36.0pt;mso-add-space:auto;text-indent:-18.0pt;mso-list:l0 level1 lfo1;tab-stops:list 36.0pt"><span lang="EN-US" style="color:black;mso-ansi-language:EN-US"><span style="mso-list:Ignore">1.<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang="EN-US" style="font-family:Arial;mso-fareast-font-family:Arial;mso-bidi-font-family:Arial;color:black;mso-ansi-language:EN-US">Version 7.0</span><span lang="EN-US" style="font-family:Arial;mso-fareast-font-family:Arial;mso-bidi-font-family:Arial;color:black;mso-ansi-language:EN-US">:<o:p></o:p></span></p>' });
LegacyUnit.equal(editor.getContent(), '<ol><li>Version 7.0:</li></ol>');
});
suite.test('Paste Word fake list before BR', function (editor) {
let rng = editor.dom.createRng();
editor.setContent('<p>1234</p>');
rng.setStart(editor.getBody().firstChild.firstChild, 0);
rng.setEnd(editor.getBody().firstChild.firstChild, 4);
editor.selection.setRng(rng);
editor.execCommand('mceInsertContent', false, '<br>a');
rng = editor.dom.createRng();
rng.setStart(editor.getBody().firstChild, 0);
rng.setEnd(editor.getBody().firstChild, 0);
editor.selection.setRng(rng);
editor.execCommand('mceInsertClipboardContent', false, { content: Strings.wordList1 });
LegacyUnit.equal(editor.getContent(), '<ul><li>Item 1</li><li>Item 2</li><li>Item 3</li><li>Item 4</li><li>Item 5</li><li>Item 6</li></ul><p><br />a</p>');
});
suite.test('Paste Word fake lists interrupted by header', function (editor) {
const rng = editor.dom.createRng();
editor.setContent('<p>1234</p>');
rng.setStart(editor.getBody().firstChild.firstChild, 0);
rng.setEnd(editor.getBody().firstChild.firstChild, 4);
editor.selection.setRng(rng);
editor.execCommand('mceInsertClipboardContent', false, { content: '<p class=MsoListParagraphCxSpFirst style=\'text-indent:-.25in;mso-list:l0 level1 lfo1\'><![if !supportLists]><span style=\'font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol\'><span style=\'mso-list:Ignore\'>·<span style=\'font:7.0pt "Times New Roman"\'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]>List before heading A<o:p></o:p></p> <p class=MsoListParagraphCxSpLast style=\'text-indent:-.25in;mso-list:l0 level1 lfo1\'><![if !supportLists]><span style=\'font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol\'><span style=\'mso-list:Ignore\'>·<span style=\'font:7.0pt "Times New Roman"\'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]>List before heading B<o:p></o:p></p> <h1>heading<o:p></o:p></h1> <p class=MsoListParagraphCxSpFirst style=\'text-indent:-.25in;mso-list:l0 level1 lfo1\'><![if !supportLists]><span style=\'font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol\'><span style=\'mso-list:Ignore\'>·<span style=\'font:7.0pt "Times New Roman"\'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]>List after heading A<o:p></o:p></p> <p class=MsoListParagraphCxSpLast style=\'text-indent:-.25in;mso-list:l0 level1 lfo1\'><![if !supportLists]><span style=\'font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol\'><span style=\'mso-list:Ignore\'>·<span style=\'font:7.0pt "Times New Roman"\'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]>List after heading B<o:p></o:p></p>' });
LegacyUnit.equal(editor.getContent(), '<ul><li>List before heading A</li><li>List before heading B</li></ul><h1>heading</h1><ul><li>List after heading A</li><li>List after heading B</li></ul>');
});
suite.test('Paste list like paragraph and list', function (editor) {
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, {
content: '<p class=MsoNormal><span style=\'font-size:10.0pt;line-height:115%;font-family:"Trebuchet MS","sans-serif";color:#666666\'>ABC. X<o:p></o:p></span></p><p class=MsoListParagraph style=\'text-indent:-.25in;mso-list:l0 level1 lfo1\'><![if !supportLists]><span style=\'mso-fareast-font-family:Calibri;mso-fareast-theme-font:minor-latin;mso-bidi-font-family:Calibri;mso-bidi-theme-font:minor-latin\'><span style=\'mso-list:Ignore\'>1.<span style=\'font:7.0pt "Times New Roman"\'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span></span><![endif]>Y</p>'
});
LegacyUnit.equal(editor.getContent(), '<p>ABC. X</p><ol><li>Y</li></ol>');
});
suite.test('Paste list like paragraph and list (disabled)', function (editor) {
editor.setContent('');
editor.settings.paste_convert_word_fake_lists = false;
editor.execCommand('mceInsertClipboardContent', false, {
content: '<p class=MsoNormal><span style=\'font-size:10.0pt;line-height:115%;font-family:"Trebuchet MS","sans-serif";color:#666666\'>ABC. X<o:p></o:p></span></p><p class=MsoListParagraph style=\'text-indent:-.25in;mso-list:l0 level1 lfo1\'><![if !supportLists]><span style=\'mso-fareast-font-family:Calibri;mso-fareast-theme-font:minor-latin;mso-bidi-font-family:Calibri;mso-bidi-theme-font:minor-latin\'><span style=\'mso-list:Ignore\'>1.<span style=\'font:7.0pt "Times New Roman"\'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span></span><![endif]>Y</p>'
});
delete editor.settings.paste_convert_word_fake_lists;
LegacyUnit.equal(editor.getContent(), '<p>ABC. X</p><p>1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Y</p>');
});
suite.test('Paste Word table', function (editor) {
const rng = editor.dom.createRng();
editor.setContent('<p>1234</p>');
rng.setStart(editor.getBody().firstChild.firstChild, 0);
rng.setEnd(editor.getBody().firstChild.firstChild, 4);
editor.selection.setRng(rng);
editor.execCommand('mceInsertClipboardContent', false, { content: Strings.table });
LegacyUnit.equal(editor.getContent(), '<table><tbody><tr><td width="307"><p>Cell 1</p></td><td width="307"><p>Cell 2</p></td></tr><tr><td width="307"><p>Cell 3</p></td><td width="307"><p>Cell 4</p></td></tr></tbody></table><p>&nbsp;</p>');
});
suite.test('Paste Office 365', function (editor) {
const rng = editor.dom.createRng();
editor.setContent('<p>1234</p>');
rng.setStart(editor.getBody().firstChild.firstChild, 0);
rng.setEnd(editor.getBody().firstChild.firstChild, 4);
editor.selection.setRng(rng);
editor.execCommand('mceInsertClipboardContent', false, { content: '<div class="OutlineElement Ltr SCX195156559">Test</div>' });
LegacyUnit.equal(editor.getContent(), '<p>Test</p>');
});
suite.test('Paste Google Docs 1', function (editor) {
const rng = editor.dom.createRng();
editor.setContent('<p>1234</p>');
rng.setStart(editor.getBody().firstChild.firstChild, 0);
rng.setEnd(editor.getBody().firstChild.firstChild, 4);
editor.selection.setRng(rng);
editor.execCommand('mceInsertClipboardContent', false, { content: '<span id="docs-internal-guid-94e46f1a-1c88-b42b-d502-1d19da30dde7"></span><p dir="ltr>Test</p>' });
LegacyUnit.equal(editor.getContent(), '<p>Test</p>');
});
suite.test('Paste Google Docs 2', function (editor) {
const rng = editor.dom.createRng();
editor.setContent('<p>1234</p>');
rng.setStart(editor.getBody().firstChild.firstChild, 0);
rng.setEnd(editor.getBody().firstChild.firstChild, 4);
editor.selection.setRng(rng);
editor.execCommand('mceInsertClipboardContent', false, {
content: (
'<meta charset="utf-8">' +
'<b style="font-weight:normal;" id="docs-internal-guid-adeb6845-fec6-72e6-6831-5e3ce002727c">' +
'<p dir="ltr">a</p>' +
'<p dir="ltr">b</p>' +
'<p dir="ltr">c</p>' +
'</b>' +
'<br class="Apple-interchange-newline">'
)
});
LegacyUnit.equal(editor.getContent(), '<p>a</p><p>b</p><p>c</p>');
});
suite.test('Paste Word without mso markings', function (editor) {
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, {
content: (
'<font face="Times New Roman" size="3"></font>' +
'<p style="margin: 0in 0in 10pt;">' +
'<span style=\'line-height: 115%; font-family: "Comic Sans MS"; font-size: 22pt;\'>Comic Sans MS</span>' +
'</p>' +
'<font face="Times New Roman" size="3"></font>'
)
});
LegacyUnit.equal(editor.getContent(), (
'<p>Comic Sans MS</p>'
));
});
suite.test('Paste Word links', function (editor) {
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, {
content: (
'<p class="MsoNormal">' +
'<a href="file:///C:/somelocation/filename.doc#_Toc238571849">1</a>' +
'<a href="#_Toc238571849">2</a>' +
'<a name="Toc238571849">3</a>' +
'<a name="_Toc238571849">4</a>' +
'<a href="#_ftn238571849" name="_ftnref238571849">[5]</a>' +
'<a href="#_ftnref238571849" name="_ftn238571849">[5]</a>' +
'<a href="#_edn238571849" name="_ednref238571849">[6]</a>' +
'<a href="#_ednref238571849" name="_edn238571849">[7]</a>' +
'<a href="http://domain.tinymce.com/someurl">8</a>' +
'<a name="#unknown">9</a>' +
'<a href="http://domain.tinymce.com/someurl" name="named_link">named_link</a>' +
'<a>5</a>' +
'</p>'
)
});
LegacyUnit.equal(editor.getContent(), (
'<p>' +
'<a href="#_Toc238571849">1</a>' +
'<a href="#_Toc238571849">2</a>' +
'<a name="Toc238571849"></a>3' +
'<a name="_Toc238571849"></a>4' +
'<a href="#_ftn238571849" name="_ftnref238571849">[5]</a>' +
'<a href="#_ftnref238571849" name="_ftn238571849">[5]</a>' +
'<a href="#_edn238571849" name="_ednref238571849">[6]</a>' +
'<a href="#_ednref238571849" name="_edn238571849">[7]</a>' +
'<a href="http://domain.tinymce.com/someurl">8</a>' +
'9' +
'named_link' +
'5' +
'</p>'
));
});
suite.test('Paste Word retain styles', function (editor) {
editor.settings.paste_retain_style_properties = 'color,background-color,font-family';
// Test color
editor.setContent('');
editor.execCommand('SelectAll');
editor.execCommand('mceInsertClipboardContent', false, { content: '<p class="MsoNormal" style="color: #ff0000">Test</p>' });
LegacyUnit.equal(editor.getContent(), '<p style=\"color: #ff0000;\">Test</p>');
// Test background-color
editor.setContent('');
editor.execCommand('SelectAll');
editor.execCommand('mceInsertClipboardContent', false, { content: '<p class="MsoNormal" style="background-color: #ff0000">Test</p>' });
LegacyUnit.equal(editor.getContent(), '<p style=\"background-color: #ff0000;\">Test</p>');
});
suite.test('Paste Word retain bold/italic styles to elements', function (editor) {
editor.settings.paste_retain_style_properties = 'color';
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, {
content: (
'<p class="MsoNormal">' +
'<span style="font-weight: bold">bold</span>' +
'<span style="font-style: italic">italic</span>' +
'<span style="font-weight: bold; font-style: italic">bold + italic</span>' +
'<span style="font-weight: bold; color: red">bold + color</span>' +
'</p>'
)
});
LegacyUnit.equal(editor.getContent(), '<p><strong>bold</strong><em>italic</em><strong><em>bold + italic</em></strong><strong><span style="color: red;">bold + color</span></strong></p>');
});
suite.test('paste track changes comment', function (editor) {
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, {
content: (
'<p class="MsoNormal">1</p>' +
'<div style="mso-element: comment;">2</div>' +
'<span class="msoDel">3</span>' +
'<del>4</del>'
)
});
LegacyUnit.equal(editor.getContent(), '<p>1</p>');
});
suite.test('paste nested (UL) word list', function (editor) {
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, {
content: (
'<p class=MsoListParagraphCxSpFirst style=\'text-indent:-18.0pt;mso-list:l0 level1 lfo1\'>' +
'<![if !supportLists]><span style=\'font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family:Symbol\'>' +
'<span style=\'mso-list:Ignore\'>·<span style=\'font:7.0pt "Times New Roman"\'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
'</span></span></span><![endif]>a</p>' +
'<p class=MsoListParagraphCxSpMiddle style=\'margin-left:72.0pt;mso-add-space:auto;text-indent:-18.0pt;mso-list:l0 level2 lfo1\'>' +
'<![if !supportLists]><span style=\'font-family:"Courier New";mso-fareast-font-family:"Courier New"\'>' +
'<span style=\'mso-list:Ignore\'>o<span style=\'font:7.0pt "Times New Roman"\'>&nbsp;&nbsp;</span></span></span><![endif]>b</p>' +
'<p class=MsoListParagraphCxSpLast style=\'margin-left:108.0pt;mso-add-space:auto;text-indent:-18.0pt;mso-list:l0 level3 lfo1\'>' +
'<![if !supportLists]><span style=\'font-family:Wingdings;mso-fareast-font-family:Wingdings;mso-bidi-font-family:Wingdings\'>' +
'<span style=\'mso-list:Ignore\'>§<span style=\'font:7.0pt "Times New Roman"\'>&nbsp;</span></span></span><![endif]>c 1. x</p>'
)
});
LegacyUnit.equal(
editor.getContent(),
'<ul>' +
'<li>a' +
'<ul>' +
'<li>b' +
'<ul>' +
'<li>c 1. x</li>' +
'</ul>' +
'</li>' +
'</ul>' +
'</li>' +
'</ul>'
);
});
suite.test('paste nested (OL) word list', function (editor) {
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, {
content: (
'<p class=MsoListParagraphCxSpFirst style=\'text-indent:-18.0pt;mso-list:l0 level1 lfo1\'>' +
'<![if !supportLists]><span style=\'mso-bidi-font-family:Calibri;mso-bidi-theme-font:minor-latin\'>' +
'<span style=\'mso-list:Ignore\'>1.<span style=\'font:7.0pt "Times New Roman"\'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>' +
'</span></span><![endif]>a</p>' +
'<p class=MsoListParagraphCxSpMiddle style=\'margin-left:72.0pt;mso-add-space:auto;text-indent:-18.0pt;mso-list:l0 level2 lfo1\'>' +
'<![if !supportLists]><span style=\'mso-bidi-font-family:Calibri;mso-bidi-theme-font:minor-latin\'><span style=\'mso-list:Ignore\'>a.' +
'<span style=\'font:7.0pt "Times New Roman"\'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span></span><![endif]>b</p>' +
'<p class=MsoListParagraphCxSpLast style=\'margin-left:108.0pt;mso-add-space:auto;text-indent:-108.0pt;mso-text-indent-alt:-9.0pt;mso-list:l0 level3 lfo1\'>' +
'<![if !supportLists]><span style=\'mso-bidi-font-family:Calibri;mso-bidi-theme-font:minor-latin\'><span style=\'mso-list:Ignore\'>' +
'<span style=\'font:7.0pt "Times New Roman"\'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>i.<span style=\'font:7.0pt "Times New Roman"\'>' +
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span></span><![endif]>c</p>'
)
});
LegacyUnit.equal(
editor.getContent(),
'<ol>' +
'<li>a' +
'<ol>' +
'<li>b' +
'<ol>' +
'<li>c</li>' +
'</ol>' +
'</li>' +
'</ol>' +
'</li>' +
'</ol>'
);
});
suite.test('Paste list start index', function (editor) {
editor.settings.paste_merge_formats = true;
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, {
content: (
'<p class=MsoListParagraphCxSpMiddle style="text-indent:-18.0pt;mso-list:l0 level1 lfo1">' +
'<![if !supportLists]><span style="mso-fareast-font-family:Calibri;mso-fareast-theme-font:minor-latin;' +
'mso-bidi-font-family:Calibri;mso-bidi-theme-font:minor-latin"><span style="mso-list:Ignore">10.' +
'<span style="font:7.0pt Times>&nbsp;&nbsp;</span></span></span><![endif]>J<o:p></o:p></p>'
)
});
LegacyUnit.equal(editor.getContent(), '<ol start="10"><li>J</li></ol>');
});
suite.test('Paste paste_merge_formats: true', function (editor) {
editor.settings.paste_merge_formats = true;
editor.setContent('<p><strong>a</strong></p>');
LegacyUnit.setSelection(editor, 'p', 1);
editor.execCommand('mceInsertClipboardContent', false, { content: '<em><strong>b</strong></em>' });
LegacyUnit.equal(editor.getContent(), '<p><strong>a<em>b</em></strong></p>');
});
suite.test('Paste paste_merge_formats: false', function (editor) {
editor.settings.paste_merge_formats = false;
editor.setContent('<p><strong>a</strong></p>');
LegacyUnit.setSelection(editor, 'p', 1);
editor.execCommand('mceInsertClipboardContent', false, { content: '<em><strong>b</strong></em>' });
LegacyUnit.equal(editor.getContent(), '<p><strong>a<em><strong>b</strong></em></strong></p>');
});
suite.test('Paste word DIV as P', function (editor) {
editor.setContent('');
editor.execCommand('SelectAll');
editor.execCommand('mceInsertClipboardContent', false, { content: '<p class="MsoNormal">1</p><div>2</div>' });
LegacyUnit.equal(editor.getContent(), '<p>1</p><p>2</p>');
});
if (Env.ie) {
suite.test('Paste part of list from IE', function (editor) {
editor.setContent('');
editor.execCommand('SelectAll');
editor.execCommand('mceInsertClipboardContent', false, { content: '<li>item2</li><li>item3</li>' });
LegacyUnit.equal(trimContent(editor.getContent()), '<ul><li>item2</li><li>item3</li></ul>', 'List tags are inferred when pasting LI');
});
}
suite.test('Disable default filters', function (editor) {
editor.settings.paste_enable_default_filters = false;
// Test color
editor.setContent('');
editor.execCommand('SelectAll');
editor.execCommand('mceInsertClipboardContent', false, { content: '<p class="MsoNormal" style="color: #ff0000;">Test</p>' });
LegacyUnit.equal(editor.getContent(), '<p class="MsoNormal" style="color: #ff0000;">Test</p>');
});
suite.test('paste invalid content with spans on page', function (editor) {
const startingContent = '<p>123 testing <span id="x">span later in document</span></p>',
insertedContent = '<ul><li>u</li><li>l</li></ul>';
editor.setContent(startingContent);
const rng = editor.dom.createRng();
rng.setStart(editor.dom.select('p')[0].firstChild, 0);
rng.setEnd(editor.dom.select('p')[0].firstChild, 0);
editor.selection.setRng(rng);
editor.execCommand('mceInsertClipboardContent', false, { content: insertedContent });
LegacyUnit.equal(editor.getContent(), insertedContent + startingContent);
});
suite.test('paste plain text with space', function (editor) {
editor.setContent('<p>text</p>');
const rng = editor.dom.createRng();
rng.setStart(editor.dom.select('p')[0].firstChild, 1);
rng.setEnd(editor.dom.select('p')[0].firstChild, 2);
editor.selection.setRng(rng);
editor.execCommand('mceInsertClipboardContent', false, { text: ' a ' });
LegacyUnit.equal(editor.getContent(), '<p>t a xt</p>');
});
suite.test('paste plain text with linefeeds', function (editor) {
editor.setContent('<p>text</p>');
const rng = editor.dom.createRng();
rng.setStart(editor.dom.select('p')[0].firstChild, 1);
rng.setEnd(editor.dom.select('p')[0].firstChild, 2);
editor.selection.setRng(rng);
editor.execCommand('mceInsertClipboardContent', false, { text: 'a\nb\nc ' });
LegacyUnit.equal(editor.getContent(), '<p>ta<br />b<br />c xt</p>');
});
suite.test('paste plain text with double linefeeds', function (editor) {
editor.setContent('<p>text</p>');
const rng = editor.dom.createRng();
rng.setStart(editor.dom.select('p')[0].firstChild, 1);
rng.setEnd(editor.dom.select('p')[0].firstChild, 2);
editor.selection.setRng(rng);
editor.execCommand('mceInsertClipboardContent', false, { text: 'a\n\nb\n\nc' });
LegacyUnit.equal(editor.getContent(), '<p>t</p><p>a</p><p>b</p><p>c</p><p>xt</p>');
});
suite.test('paste plain text with entities', function (editor) {
editor.setContent('<p>text</p>');
const rng = editor.dom.createRng();
rng.setStart(editor.dom.select('p')[0].firstChild, 1);
rng.setEnd(editor.dom.select('p')[0].firstChild, 2);
editor.selection.setRng(rng);
editor.execCommand('mceInsertClipboardContent', false, { text: '< & >' });
LegacyUnit.equal(editor.getContent(), '<p>t&lt; &amp; &gt;xt</p>');
});
suite.test('paste plain text with paragraphs', function (editor) {
editor.setContent('<p>text</p>');
const rng = editor.dom.createRng();
rng.setStart(editor.dom.select('p')[0].firstChild, 1);
rng.setEnd(editor.dom.select('p')[0].firstChild, 2);
editor.selection.setRng(rng);
editor.execCommand('mceInsertClipboardContent', false, { text: 'a\n<b>b</b>\n\nc' });
LegacyUnit.equal(editor.getContent(), '<p>t</p><p>a<br />&lt;b&gt;b&lt;/b&gt;</p><p>c</p><p>xt</p>');
});
suite.test('paste data image with paste_data_images: false', function (editor) {
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, { content: '<img src="data:image/gif;base64,R0lGODlhAQABAPAAAP8REf///yH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==">' });
LegacyUnit.equal(editor.getContent(), '');
editor.execCommand('mceInsertClipboardContent', false, { content: '<img alt="alt" src="data:image/gif;base64,R0lGODlhAQABAPAAAP8REf///yH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==">' });
LegacyUnit.equal(editor.getContent(), '');
});
suite.test('paste data image with paste_data_images: true', function (editor) {
editor.settings.paste_data_images = true;
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, { content: '<img src="data:image/gif;base64,R0lGODlhAQABAPAAAP8REf///yH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==">' });
LegacyUnit.equal(editor.getContent(), '<p><img src="data:image/gif;base64,R0lGODlhAQABAPAAAP8REf///yH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" /></p>');
});
suite.test('paste pre process text (event)', function (editor) {
function callback(e) {
e.content = 'PRE:' + e.content;
}
editor.setContent('<p>a</p>');
LegacyUnit.setSelection(editor, 'p', 0, 'p', 1);
editor.on('PastePreProcess', callback);
editor.execCommand('mceInsertClipboardContent', false, { text: 'b\n2' });
LegacyUnit.equal(editor.getContent(), '<p>PRE:b<br />2</p>');
editor.setContent('<p>a</p>');
LegacyUnit.setSelection(editor, 'p', 0, 'p', 1);
editor.off('PastePreProcess', callback);
editor.execCommand('mceInsertClipboardContent', false, { text: 'c' });
LegacyUnit.equal(editor.getContent(), '<p>c</p>');
});
suite.test('paste pre process html (event)', function (editor) {
function callback(e) {
e.content = 'PRE:' + e.content;
}
editor.setContent('<p>a</p>');
LegacyUnit.setSelection(editor, 'p', 0, 'p', 1);
editor.on('PastePreProcess', callback);
editor.execCommand('mceInsertClipboardContent', false, { content: '<em>b</em>' });
LegacyUnit.equal(editor.getContent(), '<p>PRE:<em>b</em></p>');
editor.setContent('<p>a</p>');
LegacyUnit.setSelection(editor, 'p', 0, 'p', 1);
editor.off('PastePreProcess', callback);
editor.execCommand('mceInsertClipboardContent', false, { content: '<em>c</em>' });
LegacyUnit.equal(editor.getContent(), '<p><em>c</em></p>');
});
suite.test('paste post process (event)', function (editor) {
function callback(e) {
e.node.innerHTML += ':POST';
}
editor.setContent('<p>a</p>');
LegacyUnit.setSelection(editor, 'p', 0, 'p', 1);
editor.on('PastePostProcess', callback);
editor.execCommand('mceInsertClipboardContent', false, { content: '<em>b</em>' });
LegacyUnit.equal(editor.getContent(), '<p><em>b</em>:POST</p>');
editor.setContent('<p>a</p>');
LegacyUnit.setSelection(editor, 'p', 0, 'p', 1);
editor.off('PastePostProcess', callback);
editor.execCommand('mceInsertClipboardContent', false, { content: '<em>c</em>' });
LegacyUnit.equal(editor.getContent(), '<p><em>c</em></p>');
});
suite.test('paste innerText of conditional comments', function () {
LegacyUnit.equal(Utils.innerText('<![if !supportLists]>X<![endif]>'), 'X');
});
suite.test('paste innerText of single P', function (editor) {
editor.setContent('<p>a</p>');
LegacyUnit.equal(Utils.innerText(editor.getBody().innerHTML), 'a');
});
suite.test('paste innerText of single P with whitespace wrapped content', function (editor) {
editor.setContent('<p> a </p>');
LegacyUnit.equal(Utils.innerText(editor.getBody().innerHTML), 'a');
});
suite.test('paste innerText of two P', function (editor) {
editor.setContent('<p>a</p><p>b</p>');
LegacyUnit.equal(Utils.innerText(editor.getBody().innerHTML), 'a\n\nb');
});
suite.test('paste innerText of H1 and P', function (editor) {
editor.setContent('<h1>a</h1><p>b</p>');
LegacyUnit.equal(Utils.innerText(editor.getBody().innerHTML), 'a\nb');
});
suite.test('paste innerText of P with BR', function (editor) {
editor.setContent('<p>a<br>b</p>');
LegacyUnit.equal(Utils.innerText(editor.getBody().innerHTML), 'a\nb');
});
suite.test('paste innerText of P with WBR', function (editor) {
editor.setContent('<p>a<wbr>b</p>');
LegacyUnit.equal(Utils.innerText(editor.getBody().innerHTML), 'ab');
});
suite.test('paste innerText of P with VIDEO', function (editor) {
editor.setContent('<p>a<video>b<br>c</video>d</p>');
LegacyUnit.equal(Utils.innerText(editor.getBody().innerHTML), 'a d');
});
suite.test('paste innerText of PRE', function (editor) {
editor.getBody().innerHTML = '<pre>a\nb\n</pre>';
LegacyUnit.equal(Utils.innerText(editor.getBody().innerHTML).replace(/\r\n/g, '\n'), 'a\nb\n');
});
suite.test('paste innerText of textnode with whitespace', function (editor) {
editor.getBody().innerHTML = '<pre> a </pre>';
LegacyUnit.equal(Utils.innerText(editor.getBody().firstChild.innerHTML), ' a ');
});
suite.test('trim html from clipboard fragments', function () {
LegacyUnit.equal(Utils.trimHtml('<!--StartFragment-->a<!--EndFragment-->'), 'a');
LegacyUnit.equal(Utils.trimHtml('a\n<body>\n<!--StartFragment-->\nb\n<!--EndFragment-->\n</body>\nc'), '\nb\n');
LegacyUnit.equal(Utils.trimHtml('a<!--StartFragment-->b<!--EndFragment-->c'), 'abc');
LegacyUnit.equal(Utils.trimHtml('a<body>b</body>c'), 'b');
LegacyUnit.equal(Utils.trimHtml('<HTML><HEAD><TITLE>a</TITLE></HEAD><BODY>b</BODY></HTML>'), 'b');
LegacyUnit.equal(Utils.trimHtml('a<span class="Apple-converted-space">\u00a0<\/span>b'), 'a b');
LegacyUnit.equal(Utils.trimHtml('<span class="Apple-converted-space">\u00a0<\/span>b'), ' b');
LegacyUnit.equal(Utils.trimHtml('a<span class="Apple-converted-space">\u00a0<\/span>'), 'a ');
LegacyUnit.equal(Utils.trimHtml('<span class="Apple-converted-space">\u00a0<\/span>'), ' ');
});
if (Env.ie) {
suite.test('paste font and u in anchor', function (editor) {
editor.setContent('<p>a</p>');
LegacyUnit.setSelection(editor, 'p', 1);
editor.execCommand('mceInsertClipboardContent', false, {
content: '<p><a href="#"><font size="3"><u>b</u></font></a></p>'
});
LegacyUnit.equal(editor.getContent(), '<p>a</p><p><a href="#">b</a></p>');
});
}
if (Env.webkit) {
suite.test('paste webkit retains text styles runtime styles internal', function (editor) {
editor.settings.paste_webkit_styles = 'color';
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, { content: '&lt;span style="color:red"&gt;&lt;span data-mce-style="color:red"&gt;' });
LegacyUnit.equal(editor.getContent(), '<p>&lt;span style="color:red"&gt;&lt;span data-mce-style="color:red"&gt;</p>');
});
suite.test('paste webkit remove runtime styles internal', function (editor) {
editor.settings.paste_webkit_styles = 'color';
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, { content: '<span style="color:red; font-size: 42px" data-mce-style="color: red;">Test</span>' });
LegacyUnit.equal(editor.getContent(), '<p><span style="color: red;">Test</span></p>');
});
suite.test('paste webkit remove runtime styles (color)', function (editor) {
editor.settings.paste_webkit_styles = 'color';
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, { content: '<span style="color:red; text-indent: 10px">Test</span>' });
LegacyUnit.equal(editor.getContent(), '<p><span style="color: red;">Test</span></p>');
});
suite.test('paste webkit remove runtime styles keep before attr', function (editor) {
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, { content: '<span class="c" style="color:red; text-indent: 10px">Test</span>' });
LegacyUnit.equal(editor.getContent(), '<p><span class="c">Test</span></p>');
});
suite.test('paste webkit remove runtime styles keep after attr', function (editor) {
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, { content: '<span style="color:red; text-indent: 10px" title="t">Test</span>' });
LegacyUnit.equal(editor.getContent(), '<p><span title="t">Test</span></p>');
});
suite.test('paste webkit remove runtime styles keep before/after attr', function (editor) {
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, { content: '<span class="c" style="color:red; text-indent: 10px" title="t">Test</span>' });
LegacyUnit.equal(editor.getContent(), '<p><span class="c" title="t">Test</span></p>');
});
suite.test('paste webkit remove runtime styles (background-color)', function (editor) {
editor.settings.paste_webkit_styles = 'background-color';
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, { content: '<span style="background-color:red; text-indent: 10px">Test</span>' });
LegacyUnit.equal(editor.getContent(), '<p><span style="background-color: red;">Test</span></p>');
});
suite.test('paste webkit remove runtime styles (font-size)', function (editor) {
editor.settings.paste_webkit_styles = 'font-size';
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, { content: '<span style="font-size:42px; text-indent: 10px">Test</span>' });
LegacyUnit.equal(editor.getContent(), '<p><span style="font-size: 42px;">Test</span></p>');
});
suite.test('paste webkit remove runtime styles (font-family)', function (editor) {
editor.settings.paste_webkit_styles = 'font-family';
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, { content: '<span style="font-family:Arial; text-indent: 10px">Test</span>' });
LegacyUnit.equal(editor.getContent(), '<p><span style="font-family: Arial;">Test</span></p>');
});
suite.test('paste webkit remove runtime styles font-family allowed but not specified', function (editor) {
editor.settings.paste_webkit_styles = 'font-family';
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, { content: '<p title="x" style="text-indent: 10px">Test</p>' });
LegacyUnit.equal(editor.getContent(), '<p title="x">Test</p>');
});
suite.test('paste webkit remove runtime styles (custom styles)', function (editor) {
editor.settings.paste_webkit_styles = 'color font-style';
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, { content: '<span style="color: red; font-style: italic; text-indent: 10px">Test</span>' });
LegacyUnit.equal(editor.getContent(), '<p><span style="color: red; font-style: italic;">Test</span></p>');
});
suite.test('paste webkit remove runtime styles (all)', function (editor) {
editor.settings.paste_webkit_styles = 'all';
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, { content: '<span style="color: red; font-style: italic; text-indent: 10px">Test</span>' });
LegacyUnit.equal(editor.getContent(), '<p><span style=\"color: red; font-style: italic; text-indent: 10px;\">Test</span></p>');
});
suite.test('paste webkit remove runtime styles (none)', function (editor) {
editor.settings.paste_webkit_styles = 'none';
editor.setContent('');
editor.execCommand('mceInsertClipboardContent', false, { content: '<span style="color: red; font-style: italic; text-indent: 10px">Test</span>' });
LegacyUnit.equal(editor.getContent(), '<p>Test</p>');
});
suite.test('paste webkit remove runtime styles (color) in the same (color) (named)', function (editor) {
editor.settings.paste_webkit_styles = 'color';
editor.setContent('<p style="color:red">Test</span>');
LegacyUnit.setSelection(editor, 'p', 0, 'p', 4);
editor.execCommand('mceInsertClipboardContent', false, {
content: (
'<span style="color:#ff0000; text-indent: 10px">a</span>' +
'<span style="color:rgb(255, 0, 0); text-indent: 10px">b</span>'
)
});
LegacyUnit.equal(editor.getContent(), '<p style="color: red;">ab</p>');
});
suite.test('paste webkit remove runtime styles (color) in the same (color) (hex)', function (editor) {
editor.setContent('<p style="color:#ff0000">Test</span>');
LegacyUnit.setSelection(editor, 'p', 0, 'p', 4);
editor.execCommand('mceInsertClipboardContent', false, {
content: (
'<span style="color:red; text-indent: 10px">a</span>' +
'<span style="color:#ff0000; text-indent: 10px">b</span>' +
'<span style="color:rgb(255, 0, 0); text-indent: 10px">c</span>'
)
});
LegacyUnit.equal(editor.getContent(), '<p style="color: #ff0000;">abc</p>');
});
suite.test('paste webkit remove runtime styles (color) in the same (color) (rgb)', function (editor) {
editor.setContent('<p style="color:rgb(255, 0, 0)">Test</span>');
LegacyUnit.setSelection(editor, 'p', 0, 'p', 4);
editor.execCommand('mceInsertClipboardContent', false, {
content: (
'<span style="color:red; text-indent: 10px">a</span>' +
'<span style="color:#ff0000; text-indent: 10px">b</span>' +
'<span style="color:rgb(255, 0, 0); text-indent: 10px">c</span>'
)
});
LegacyUnit.equal(editor.getContent(), '<p style="color: #ff0000;">abc</p>');
});
}
TinyLoader.setup(function (editor, onSuccess, onFailure) {
Pipeline.async({}, appendTeardown(editor, suite.toSteps(editor)), onSuccess, onFailure);
}, {
add_unload_trigger: false,
indent: false,
plugins: 'paste',
skin_url: '/project/js/tinymce/skins/lightgray'
}, success, failure);
});

View File

@@ -0,0 +1,147 @@
import { Assertions, Chain, Guard, Pipeline } from '@ephox/agar';
import { UnitTest } from '@ephox/bedrock';
import { Id, Merger, Obj } from '@ephox/katamari';
import EditorManager from 'tinymce/core/api/EditorManager';
import PastePlugin from 'tinymce/plugins/paste/Plugin';
import Theme from 'tinymce/themes/modern/Theme';
import MockDataTransfer from '../module/test/MockDataTransfer';
import ViewBlock from '../module/test/ViewBlock';
UnitTest.asynctest('tinymce.plugins.paste.browser.PlainTextPaste', function () {
const success = arguments[arguments.length - 2];
const failure = arguments[arguments.length - 1];
const viewBlock = ViewBlock();
const cCreateEditorFromSettings = function (settings, html?) {
return Chain.async(function (viewBlock: any, next, die) {
const randomId = Id.generate('tiny-');
html = html || '<textarea></textarea>';
viewBlock.update(html);
viewBlock.get().firstChild.id = randomId;
EditorManager.init(Merger.merge(settings, {
selector: '#' + randomId,
skin_url: '/project/js/tinymce/skins/lightgray',
indent: false,
setup (editor) {
editor.on('SkinLoaded', function () {
setTimeout(function () {
next(editor);
}, 0);
});
}
}));
});
};
const cRemoveEditor = function () {
return Chain.op(function (editor: any) {
editor.remove();
});
};
const cClearEditor = function () {
return Chain.async(function (editor: any, next, die) {
editor.setContent('');
next(editor);
});
};
const cFireFakePasteEvent = function (data) {
return Chain.async(function (editor: any, next, die) {
editor.fire('paste', { clipboardData: MockDataTransfer.create(data) });
next(editor);
});
};
const cAssertEditorContent = function (label, expected) {
return Chain.async(function (editor: any, next, die) {
Assertions.assertHtml(label || 'Asserting editors content', expected, editor.getContent());
next(editor);
});
};
const cAssertClipboardPaste = function (expected, data) {
const chains = [];
Obj.each(data, function (data, label) {
chains.push(
cFireFakePasteEvent(data),
Chain.control(
cAssertEditorContent(label, expected),
Guard.tryUntil('Wait for paste to succeed.', 100, 1000)
),
cClearEditor()
);
});
return Chain.fromChains(chains);
};
const srcText = 'one\r\ntwo\r\n\r\nthree\r\n\r\n\r\nfour\r\n\r\n\r\n\r\n.';
const pasteData = {
Firefox: {
'text/plain': srcText,
'text/html': 'one<br>two<br><br>three<br><br><br>four<br><br><br><br>.'
},
Chrome: {
'text/plain': srcText,
'text/html': '<div>one</div><div>two</div><div><br></div><div>three</div><div><br></div><div><br></div><div>four</div><div><br></div><div><br></div><div><br></div><div>.'
},
Edge: {
'text/plain': srcText,
'text/html': '<div>one<br>two</div><div>three</div><div><br>four</div><div><br></div><div>.</div>'
},
IE: {
'text/plain': srcText,
'text/html': '<p>one<br>two</p><p>three</p><p><br>four</p><p><br></p><p>.</p>'
}
};
const expectedWithRootBlock = '<p>one<br />two</p><p>three</p><p><br />four</p><p>&nbsp;</p><p>.</p>';
const expectedWithRootBlockAndAttrs = '<p class="attr">one<br />two</p><p class="attr">three</p><p class="attr"><br />four</p><p class="attr">&nbsp;</p><p class="attr">.</p>';
const expectedWithoutRootBlock = 'one<br />two<br /><br />three<br /><br /><br />four<br /><br /><br /><br />.';
Theme();
PastePlugin();
viewBlock.attach();
Pipeline.async({}, [
Chain.asStep(viewBlock, [
cCreateEditorFromSettings({
plugins: 'paste',
forced_root_block: 'p' // default
}),
cAssertClipboardPaste(expectedWithRootBlock, pasteData),
cRemoveEditor()
]),
Chain.asStep(viewBlock, [
cCreateEditorFromSettings({
plugins: 'paste',
forced_root_block: 'p',
forced_root_block_attrs: {
class: 'attr'
}
}),
cAssertClipboardPaste(expectedWithRootBlockAndAttrs, pasteData),
cRemoveEditor()
]),
Chain.asStep(viewBlock, [
cCreateEditorFromSettings({
plugins: 'paste',
forced_root_block: false
}),
cAssertClipboardPaste(expectedWithoutRootBlock, pasteData),
cRemoveEditor()
])
], function () {
viewBlock.detach();
success();
}, failure);
});

View File

@@ -0,0 +1,116 @@
import { Assertions, Chain, Logger, Pipeline } from '@ephox/agar';
import { UnitTest } from '@ephox/bedrock';
import { Fun } from '@ephox/katamari';
import { TinyLoader } from '@ephox/mcagar';
import ProcessFilters from 'tinymce/plugins/paste/core/ProcessFilters';
import PastePlugin from 'tinymce/plugins/paste/Plugin';
import Theme from 'tinymce/themes/modern/Theme';
UnitTest.asynctest('tinymce.plugins.paste.browser.ProcessFiltersTest', function () {
const success = arguments[arguments.length - 2];
const failure = arguments[arguments.length - 1];
Theme();
PastePlugin();
const cProcessPre = function (html, internal, preProcess) {
return Chain.mapper(function (editor: any) {
editor.on('PastePreProcess', preProcess);
const result = ProcessFilters.process(editor, html, internal);
editor.off('PastePreProcess', preProcess);
return result;
});
};
const cProcessPrePost = function (html, internal, preProcess, postProcess) {
return Chain.mapper(function (editor: any) {
editor.on('PastePreProcess', preProcess);
editor.on('PastePostProcess', postProcess);
const result = ProcessFilters.process(editor, html, internal);
editor.off('PastePreProcess', preProcess);
editor.off('PastePostProcess', postProcess);
return result;
});
};
const preventHandler = function (e) {
e.preventDefault();
};
const preProcessHandler = function (e) {
e.content += 'X';
};
const postProcessHandler = function (editor) {
return function (e) {
editor.dom.remove(editor.dom.select('b', e.node), true);
};
};
const assertInternal = function (expectedFlag) {
return function (e) {
Assertions.assertEq('Should be expected internal flag', expectedFlag, e.internal);
};
};
TinyLoader.setup(function (editor, onSuccess, onFailure) {
Pipeline.async({}, [
Logger.t('Paste pre process only', Chain.asStep(editor, [
cProcessPre('a', true, preProcessHandler),
Assertions.cAssertEq('Should be preprocessed by adding a X', { content: 'aX', cancelled: false })
])),
Logger.t('Paste pre/post process passthough as is', Chain.asStep(editor, [
cProcessPrePost('a', true, Fun.noop, Fun.noop),
Assertions.cAssertEq('Should be unchanged', { content: 'a', cancelled: false })
])),
Logger.t('Paste pre/post process assert internal false', Chain.asStep(editor, [
cProcessPrePost('a', false, assertInternal(false), assertInternal(false)),
Assertions.cAssertEq('Should be unchanged', { content: 'a', cancelled: false })
])),
Logger.t('Paste pre/post process assert internal true', Chain.asStep(editor, [
cProcessPrePost('a', true, assertInternal(true), assertInternal(true)),
Assertions.cAssertEq('Should be unchanged', { content: 'a', cancelled: false })
])),
Logger.t('Paste pre/post process alter on preprocess', Chain.asStep(editor, [
cProcessPrePost('a', true, preProcessHandler, Fun.noop),
Assertions.cAssertEq('Should be preprocessed by adding a X', { content: 'aX', cancelled: false })
])),
Logger.t('Paste pre/post process alter on postprocess', Chain.asStep(editor, [
cProcessPrePost('a<b>b</b>c', true, Fun.noop, postProcessHandler(editor)),
Assertions.cAssertEq('Should have all b elements removed', { content: 'abc', cancelled: false })
])),
Logger.t('Paste pre/post process alter on preprocess/postprocess', Chain.asStep(editor, [
cProcessPrePost('a<b>b</b>c', true, preProcessHandler, postProcessHandler(editor)),
Assertions.cAssertEq('Should have all b elements removed and have a X added', { content: 'abcX', cancelled: false })
])),
Logger.t('Paste pre/post process prevent default on preProcess', Chain.asStep(editor, [
cProcessPrePost('a<b>b</b>c', true, preventHandler, postProcessHandler(editor)),
Assertions.cAssertEq('Should have all b elements removed and be cancelled', { content: 'a<b>b</b>c', cancelled: true })
])),
Logger.t('Paste pre/post process prevent default on postProcess', Chain.asStep(editor, [
cProcessPrePost('a<b>b</b>c', true, preProcessHandler, preventHandler),
Assertions.cAssertEq('Should have a X added and be cancelled', { content: 'a<b>b</b>cX', cancelled: true })
]))
], onSuccess, onFailure);
}, {
add_unload_trigger: false,
indent: false,
plugins: 'paste',
skin_url: '/project/js/tinymce/skins/lightgray'
}, success, failure);
});

View File

@@ -0,0 +1,88 @@
import { Pipeline } from '@ephox/agar';
import { UnitTest } from '@ephox/bedrock';
import { LegacyUnit, TinyLoader } from '@ephox/mcagar';
import SmartPaste from 'tinymce/plugins/paste/core/SmartPaste';
import Plugin from 'tinymce/plugins/paste/Plugin';
import Theme from 'tinymce/themes/modern/Theme';
UnitTest.asynctest('tinymce.plugins.paste.browser.ImagePasteTest', function () {
const success = arguments[arguments.length - 2];
const failure = arguments[arguments.length - 1];
const suite = LegacyUnit.createSuite();
Plugin();
Theme();
suite.test('isAbsoluteUrl', function () {
LegacyUnit.equal(SmartPaste.isAbsoluteUrl('http://www.site.com'), true);
LegacyUnit.equal(SmartPaste.isAbsoluteUrl('https://www.site.com'), true);
LegacyUnit.equal(SmartPaste.isAbsoluteUrl('http://www.site.com/dir-name/file.gif?query=%42'), true);
LegacyUnit.equal(SmartPaste.isAbsoluteUrl('https://www.site.com/dir-name/file.gif?query=%42'), true);
LegacyUnit.equal(SmartPaste.isAbsoluteUrl('https://www.site.com/dir-name/file.gif?query=%42#a'), true);
LegacyUnit.equal(SmartPaste.isAbsoluteUrl('https://www.site.com/~abc'), true);
LegacyUnit.equal(SmartPaste.isAbsoluteUrl('file.gif'), false);
LegacyUnit.equal(SmartPaste.isAbsoluteUrl(''), false);
});
suite.test('isImageUrl', function () {
LegacyUnit.equal(SmartPaste.isImageUrl('http://www.site.com'), false);
LegacyUnit.equal(SmartPaste.isImageUrl('https://www.site.com'), false);
LegacyUnit.equal(SmartPaste.isImageUrl('http://www.site.com/dir-name/file.jpeg'), true);
LegacyUnit.equal(SmartPaste.isImageUrl('http://www.site.com/dir-name/file.jpg'), true);
LegacyUnit.equal(SmartPaste.isImageUrl('http://www.site.com/dir-name/file.png'), true);
LegacyUnit.equal(SmartPaste.isImageUrl('http://www.site.com/dir-name/file.gif'), true);
LegacyUnit.equal(SmartPaste.isImageUrl('https://www.site.com/dir-name/file.gif'), true);
LegacyUnit.equal(SmartPaste.isImageUrl('https://www.site.com/~dir-name/file.gif'), true);
LegacyUnit.equal(SmartPaste.isImageUrl('https://www.site.com/dir-name/file.gif?query=%42'), false);
LegacyUnit.equal(SmartPaste.isImageUrl('https://www.site.com/dir-name/file.html?query=%42'), false);
LegacyUnit.equal(SmartPaste.isImageUrl('file.gif'), false);
LegacyUnit.equal(SmartPaste.isImageUrl(''), false);
});
suite.test('smart paste url on selection', function (editor) {
editor.focus();
editor.undoManager.clear();
editor.setContent('<p>abc</p>');
LegacyUnit.setSelection(editor, 'p', 0, 'p', 3);
editor.undoManager.add();
editor.execCommand('mceInsertClipboardContent', false, { content: 'http://www.site.com' });
LegacyUnit.equal(editor.getContent(), '<p><a href="http://www.site.com">abc</a></p>');
LegacyUnit.equal(editor.undoManager.data.length, 3);
});
suite.test('smart paste image url', function (editor) {
editor.focus();
editor.undoManager.clear();
editor.setContent('<p>abc</p>');
LegacyUnit.setSelection(editor, 'p', 1);
editor.undoManager.add();
editor.execCommand('mceInsertClipboardContent', false, { content: 'http://www.site.com/my.jpg' });
LegacyUnit.equal(editor.getContent(), '<p>a<img src="http://www.site.com/my.jpg" />bc</p>');
LegacyUnit.equal(editor.undoManager.data.length, 3);
});
suite.test('smart paste option disabled', function (editor) {
editor.focus();
editor.undoManager.clear();
editor.setContent('<p>abc</p>');
LegacyUnit.setSelection(editor, 'p', 1);
editor.undoManager.add();
editor.settings.smart_paste = false;
editor.execCommand('mceInsertClipboardContent', false, { content: 'http://www.site.com/my.jpg' });
LegacyUnit.equal(editor.getContent(), '<p>ahttp://www.site.com/my.jpgbc</p>');
LegacyUnit.equal(editor.undoManager.data.length, 2);
});
TinyLoader.setup(function (editor, onSuccess, onFailure) {
Pipeline.async({}, suite.toSteps(editor), onSuccess, onFailure);
}, {
add_unload_trigger: false,
indent: false,
plugins: 'paste',
skin_url: '/project/js/tinymce/skins/lightgray'
}, success, failure);
});

View File

@@ -0,0 +1,20 @@
import { Assertions } from '@ephox/agar';
import { Editor } from 'tinymce/core/api/Editor';
import EditorManager from 'tinymce/core/api/EditorManager';
import PluginManager from 'tinymce/core/api/PluginManager';
import DetectProPlugin from 'tinymce/plugins/paste/alien/DetectProPlugin';
import { UnitTest } from '@ephox/bedrock';
UnitTest.test('browser.tinymce.plugins.paste.alien.DetectProPluginTest', function () {
// Fake loading of powerpaste
PluginManager.add('powerpaste', function () { });
Assertions.assertEq('Should not have pro plugin', false, DetectProPlugin.hasProPlugin(new Editor('id', { plugins: 'paste' }, EditorManager)));
Assertions.assertEq('Should not have pro plugin', false, DetectProPlugin.hasProPlugin(new Editor('id', { plugins: '' }, EditorManager)));
Assertions.assertEq('Should have pro plugin', true, DetectProPlugin.hasProPlugin(new Editor('id', { plugins: 'powerpaste' }, EditorManager)));
Assertions.assertEq('Should have pro plugin', true, DetectProPlugin.hasProPlugin(new Editor('id', { plugins: 'paste powerpaste' }, EditorManager)));
Assertions.assertEq('Should have pro plugin', true, DetectProPlugin.hasProPlugin(new Editor('id', { plugins: 'powerpaste paste' }, EditorManager)));
Assertions.assertEq('Should have pro plugin', true, DetectProPlugin.hasProPlugin(new Editor('id', { plugins: 'paste powerpaste paste' }, EditorManager)));
Assertions.assertEq('Should have pro plugin', true, DetectProPlugin.hasProPlugin(new Editor('id', { plugins: 'paste,powerpaste,paste' }, EditorManager)));
Assertions.assertEq('Should have pro plugin', true, DetectProPlugin.hasProPlugin(new Editor('id', { plugins: 'paste powerpaste paste' }, EditorManager)));
});

View File

@@ -0,0 +1,61 @@
import { Arr, Obj } from '@ephox/katamari';
const notImplemented = function () {
throw new Error('Mockup function is not implemented.');
};
const createDataTransferItem = function (mime, content) {
return {
kind: 'string',
type: mime,
getAsFile: notImplemented,
getAsString () {
return content;
}
};
};
const create = function (inputData) {
let data = {}, result;
const clearData = function () {
data = {};
result.items = [];
result.types = [];
};
const getData = function (mime) {
return mime in data ? data[mime] : '';
};
const setData = function (mime, content) {
data[mime] = content;
result.types = Obj.keys(data);
result.items = Arr.map(result.types, function (type) {
return createDataTransferItem(type, data[type]);
});
};
result = {
dropEffect: '',
effectAllowed: 'all',
files: [],
items: [],
types: [],
clearData,
getData,
setData,
setDragImage: notImplemented,
addElement: notImplemented
};
Obj.each(inputData, function (value, key) {
setData(key, value);
});
return result;
};
export default {
create
};

View File

@@ -0,0 +1,13 @@
import { Step } from '@ephox/agar';
import MockDataTransfer from './MockDataTransfer';
const sPaste = function (editor, data) {
return Step.sync(function () {
const dataTransfer = MockDataTransfer.create(data);
editor.fire('paste', { clipboardData: dataTransfer });
});
};
export default {
sPaste
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,34 @@
import DOMUtils from 'tinymce/core/api/dom/DOMUtils';
import { document, HTMLElement } from '@ephox/dom-globals';
export default function () {
const domElm: HTMLElement = DOMUtils.DOM.create('div', {
style: 'position: absolute; right: 10px; top: 10px;'
});
const attach = function (preventDuplicates?) {
if (preventDuplicates && domElm.parentNode === document.body) {
detach();
}
document.body.appendChild(domElm);
};
const detach = function () {
DOMUtils.DOM.remove(domElm);
};
const update = function (html) {
DOMUtils.DOM.setHTML(domElm, html);
};
const get = function () {
return domElm;
};
return {
attach,
update,
detach,
get
};
}

View File

@@ -0,0 +1,41 @@
import { Pipeline, RealMouse, Waiter } from '@ephox/agar';
import { UnitTest } from '@ephox/bedrock';
import { TinyApis, TinyLoader, TinyUi } from '@ephox/mcagar';
import { PlatformDetection } from '@ephox/sand';
import PastePlugin from 'tinymce/plugins/paste/Plugin';
import Theme from 'tinymce/themes/modern/Theme';
import { window } from '@ephox/dom-globals';
UnitTest.asynctest('tinymce.plugins.paste.webdriver.CutTest', function () {
const success = arguments[arguments.length - 2];
const failure = arguments[arguments.length - 1];
Theme();
PastePlugin();
const platform = PlatformDetection.detect();
/* Test does not work on Phantom */
if (window.navigator.userAgent.indexOf('PhantomJS') > -1) {
return success();
}
TinyLoader.setup(function (editor, onSuccess, onFailure) {
const api = TinyApis(editor);
const ui = TinyUi(editor);
// Cut doesn't seem to work in webdriver mode on ie, firefox is producing moveto not supported, edge fails if it's not observed
Pipeline.async({}, (platform.browser.isIE() || platform.browser.isFirefox() || platform.browser.isEdge()) ? [] : [
api.sSetContent('<p>abc</p>'),
api.sSetSelection([0, 0], 1, [0, 0], 2),
ui.sClickOnMenu('Click Edit menu', 'button:contains("Edit")'),
ui.sWaitForUi('Wait for dropdown', '.mce-floatpanel[role="application"]'),
RealMouse.sClickOn('.mce-i-cut'),
Waiter.sTryUntil('Cut is async now, so need to wait for content', api.sAssertContent('<p>ac</p>'), 100, 1000)
], onSuccess, onFailure);
}, {
skin_url: '/project/js/tinymce/skins/lightgray',
plugins: 'paste'
}, success, failure);
});