import Header from '@editorjs/header'; import Image from '@editorjs/simple-image'; import * as _ from '../../../src/components/utils'; import { BlockTool, BlockToolData } from '../../../types'; import $ from '../../../src/components/dom'; describe('Copy pasting from Editor', function () { context('pasting', function () { it('should paste plain text', function () { cy.createEditor({}); cy.get('[data-cy=editorjs]') .get('div.ce-block') .as('block') .click() .paste({ // eslint-disable-next-line @typescript-eslint/naming-convention 'text/plain': 'Some plain text', }); cy.get('@block').should('contain', 'Some plain text'); }); it('should paste inline html data', function () { cy.createEditor({}); cy.get('[data-cy=editorjs]') .get('div.ce-block') .as('block') .click() .paste({ // eslint-disable-next-line @typescript-eslint/naming-convention 'text/html': '
Some text
', }); cy.get('@block').should('contain.html', 'Some text'); }); it('should paste several blocks if plain text contains new lines', function () { cy.createEditor({}); cy.get('[data-cy=editorjs]') .get('div.ce-block') .click() .paste({ // eslint-disable-next-line @typescript-eslint/naming-convention 'text/plain': 'First block\n\nSecond block', }); cy.get('[data-cy=editorjs]') .get('div.ce-block') .then(blocks => { expect(blocks[0].textContent).to.eq('First block'); expect(blocks[1].textContent).to.eq('Second block'); }); }); it('should paste several blocks if html contains several paragraphs', function () { cy.createEditor({}); cy.get('[data-cy=editorjs]') .get('div.ce-block') .click() .paste({ // eslint-disable-next-line @typescript-eslint/naming-convention 'text/html': 'First block
Second block
', }); cy.get('[data-cy=editorjs]') .get('div.ce-block') .then(blocks => { expect(blocks[0].textContent).to.eq('First block'); expect(blocks[1].textContent).to.eq('Second block'); }); }); it('should paste using custom data type', function () { cy.createEditor({}); cy.get('[data-cy=editorjs]') .get('div.ce-block') .click() .paste({ // eslint-disable-next-line @typescript-eslint/naming-convention 'application/x-editor-js': JSON.stringify([ { tool: 'paragraph', data: { text: 'First block', }, }, { tool: 'paragraph', data: { text: 'Second block', }, }, ]), }); cy.get('[data-cy=editorjs]') .get('div.ce-block') .then(blocks => { expect(blocks[0].textContent).to.eq('First block'); expect(blocks[1].textContent).to.eq('Second block'); }); }); it('should parse block tags', function () { cy.createEditor({ tools: { header: Header, }, }); cy.get('[data-cy=editorjs]') .get('div.ce-block') .click() .paste({ // eslint-disable-next-line @typescript-eslint/naming-convention 'text/html': 'Second block
', }); cy.get('[data-cy=editorjs]') .get('h2.ce-header') .should('contain', 'First block'); cy.get('[data-cy=editorjs]') .get('div.ce-paragraph') .should('contain', 'Second block'); }); it('should parse pattern', function () { cy.createEditor({ tools: { image: Image, }, }); cy.get('[data-cy=editorjs]') .get('div.ce-block') .click() .paste({ // eslint-disable-next-line @typescript-eslint/naming-convention 'text/plain': 'https://codex.so/public/app/img/external/codex2x.png', }); cy.get('[data-cy=editorjs]') // In Edge test are performed slower, so we need to increase timeout to wait until image is loaded on the page .get('img', { timeout: 10000 }) .should('have.attr', 'src', 'https://codex.so/public/app/img/external/codex2x.png'); }); it('should not prevent default behaviour if block\'s paste config equals false', function () { const onPasteStub = cy.stub().as('onPaste'); /** * Tool with disabled preventing default behavior of onPaste event */ class BlockToolWithPasteHandler implements BlockTool { public static pasteConfig = false; /** * Render block */ public render(): HTMLElement { const block = $.make('div', 'ce-block-with-disabled-prevent-default', { contentEditable: 'true', }); block.addEventListener('paste', onPasteStub); return block; } /** * Save data method */ public save(): BlockToolData { return {}; } } cy.createEditor({ tools: { blockToolWithPasteHandler: BlockToolWithPasteHandler, }, }) .as('editorInstanceWithBlockToolWithPasteHandler'); cy.get('@editorInstanceWithBlockToolWithPasteHandler') .render({ blocks: [ { type: 'blockToolWithPasteHandler', data: {}, }, ], }) .wait(100); cy.get('@editorInstanceWithBlockToolWithPasteHandler') .get('div.ce-block-with-disabled-prevent-default') .click() .paste({ // eslint-disable-next-line @typescript-eslint/naming-convention 'text/plain': 'Hello', }); cy.get('@onPaste') .should('have.been.calledWithMatch', { defaultPrevented: false, }); }); }); context('copying', function () { it('should copy inline fragment', function () { cy.createEditor({}); cy.get('[data-cy=editorjs]') .get('div.ce-block') .click() .type('Some text{selectall}') .copy() .then(clipboardData => { /** * As no blocks selected, clipboard data will be empty as will be handled by browser */ expect(clipboardData).to.be.empty; }); }); it('should copy several blocks', function () { cy.createEditor({}); cy.get('[data-cy=editorjs]') .get('div.ce-block') .click() .type('First block{enter}'); cy.get('[data-cy=editorjs') .get('div.ce-block') .next() .type('Second block') .type('{movetostart}') .trigger('keydown', { shiftKey: true, keyCode: _.keyCodes.UP, }) .copy() .then(clipboardData => { expect(clipboardData['text/html']).to.match(/First block(
)?<\/p>
Second block(
)?<\/p>/);
expect(clipboardData['text/plain']).to.eq(`First block\n\nSecond block`);
/**
* Need to wait for custom data as it is set asynchronously
*/
cy.wait(0).then(function () {
expect(clipboardData['application/x-editor-js']).not.to.be.undefined;
const data = JSON.parse(clipboardData['application/x-editor-js']);
expect(data[0].tool).to.eq('paragraph');
expect(data[0].data.text).to.match(/First block(
)?/);
expect(data[1].tool).to.eq('paragraph');
expect(data[1].data.text).to.match(/Second block(
)?/);
});
});
});
});
context('cutting', function () {
it('should cut inline fragment', function () {
cy.createEditor({});
cy.get('[data-cy=editorjs]')
.get('div.ce-block')
.click()
.type('Some text{selectall}')
.cut()
.then(clipboardData => {
/**
* As no blocks selected, clipboard data will be empty as will be handled by browser
*/
expect(clipboardData).to.be.empty;
});
});
it('should cut several blocks', function () {
cy.createEditor({
data: {
blocks: [
{
type: 'paragraph',
data: { text: 'First block' },
},
{
type: 'paragraph',
data: { text: 'Second block' },
},
],
},
});
cy.get('[data-cy=editorjs')
.get('div.ce-block')
.last()
.click()
.type('{movetostart}')
.trigger('keydown', {
shiftKey: true,
keyCode: _.keyCodes.UP,
})
.cut()
.then(clipboardData => {
expect(clipboardData['text/html']).to.match(/
First block(
)?<\/p>
Second block(
)?<\/p>/);
expect(clipboardData['text/plain']).to.eq(`First block\n\nSecond block`);
/**
* Need to wait for custom data as it is set asynchronously
*/
cy.wait(0).then(function () {
expect(clipboardData['application/x-editor-js']).not.to.be.undefined;
const data = JSON.parse(clipboardData['application/x-editor-js']);
expect(data[0].tool).to.eq('paragraph');
expect(data[0].data.text).to.match(/First block(
)?/);
expect(data[1].tool).to.eq('paragraph');
expect(data[1].data.text).to.match(/Second block(
)?/);
});
});
cy.get('[data-cy=editorjs]')
.should('not.contain', 'First block')
.should('not.contain', 'Second block');
});
it('should cut lots of blocks', function () {
const numberOfBlocks = 50;
const blocks = [];
for (let i = 0; i < numberOfBlocks; i++) {
blocks.push({
type: 'paragraph',
data: {
text: `Block ${i}`,
},
});
}
cy.createEditor({
data: {
blocks,
},
});
cy.get('[data-cy=editorjs]')
.get('div.ce-block')
.first()
.click()
.type('{ctrl+A}')
.type('{ctrl+A}')
.cut()
.then((clipboardData) => {
/**
* Need to wait for custom data as it is set asynchronously
*/
cy.wait(0).then(function () {
expect(clipboardData['application/x-editor-js']).not.to.be.undefined;
const data = JSON.parse(clipboardData['application/x-editor-js']);
expect(data.length).to.eq(numberOfBlocks);
});
});
});
});
});