| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421 |
- /**
- * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
- /* global document */
- import BlockQuote from '../src/blockquote';
- import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
- import Image from '@ckeditor/ckeditor5-image/src/image';
- import ImageCaption from '@ckeditor/ckeditor5-image/src/imagecaption';
- import List from '@ckeditor/ckeditor5-list/src/list';
- import Enter from '@ckeditor/ckeditor5-enter/src/enter';
- import Delete from '@ckeditor/ckeditor5-typing/src/delete';
- import Heading from '@ckeditor/ckeditor5-heading/src/heading';
- import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
- import {
- parse as parseModel,
- getData as getModelData,
- setData as setModelData
- } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
- describe( 'BlockQuote integration', () => {
- let editor, model, element, viewDocument;
- beforeEach( () => {
- element = document.createElement( 'div' );
- document.body.appendChild( element );
- return ClassicTestEditor
- .create( element, {
- plugins: [ BlockQuote, Paragraph, Image, ImageCaption, List, Enter, Delete, Heading ]
- } )
- .then( newEditor => {
- editor = newEditor;
- model = editor.model;
- viewDocument = editor.editing.view.document;
- } );
- } );
- afterEach( () => {
- element.remove();
- return editor.destroy();
- } );
- describe( 'enter key support', () => {
- function fakeEventData() {
- return {
- preventDefault: sinon.spy()
- };
- }
- it( 'does nothing if selection is in an empty block but not in a block quote', () => {
- const data = fakeEventData();
- const execSpy = sinon.spy( editor, 'execute' );
- setModelData( model, '<paragraph>x</paragraph><paragraph>[]</paragraph><paragraph>x</paragraph>' );
- viewDocument.fire( 'enter', data );
- // Only enter command should be executed.
- expect( data.preventDefault.called ).to.be.true;
- expect( execSpy.calledOnce ).to.be.true;
- expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'enter' );
- } );
- it( 'does nothing if selection is in a non-empty block (at the end) in a block quote', () => {
- const data = fakeEventData();
- const execSpy = sinon.spy( editor, 'execute' );
- setModelData( model, '<blockQuote><paragraph>xx[]</paragraph></blockQuote>' );
- viewDocument.fire( 'enter', data );
- // Only enter command should be executed.
- expect( data.preventDefault.called ).to.be.true;
- expect( execSpy.calledOnce ).to.be.true;
- expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'enter' );
- } );
- it( 'does nothing if selection is in a non-empty block (at the beginning) in a block quote', () => {
- const data = fakeEventData();
- const execSpy = sinon.spy( editor, 'execute' );
- setModelData( model, '<blockQuote><paragraph>[]xx</paragraph></blockQuote>' );
- viewDocument.fire( 'enter', data );
- // Only enter command should be executed.
- expect( data.preventDefault.called ).to.be.true;
- expect( execSpy.calledOnce ).to.be.true;
- expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'enter' );
- } );
- it( 'does nothing if selection is not collapsed', () => {
- const data = fakeEventData();
- const execSpy = sinon.spy( editor, 'execute' );
- setModelData( model, '<blockQuote><paragraph>[</paragraph><paragraph>]</paragraph></blockQuote>' );
- viewDocument.fire( 'enter', data );
- // Only enter command should be executed.
- expect( data.preventDefault.called ).to.be.true;
- expect( execSpy.calledOnce ).to.be.true;
- expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'enter' );
- } );
- it( 'does not interfere with a similar handler in the list feature', () => {
- const data = fakeEventData();
- setModelData( model,
- '<paragraph>x</paragraph>' +
- '<blockQuote>' +
- '<listItem listIndent="0" listType="bulleted">a</listItem>' +
- '<listItem listIndent="0" listType="bulleted">[]</listItem>' +
- '</blockQuote>' +
- '<paragraph>x</paragraph>'
- );
- viewDocument.fire( 'enter', data );
- expect( data.preventDefault.called ).to.be.true;
- expect( getModelData( model ) ).to.equal(
- '<paragraph>x</paragraph>' +
- '<blockQuote>' +
- '<listItem listIndent="0" listType="bulleted">a</listItem>' +
- '<paragraph>[]</paragraph>' +
- '</blockQuote>' +
- '<paragraph>x</paragraph>'
- );
- } );
- it( 'escapes block quote if selection is in an empty block in an empty block quote', () => {
- const data = fakeEventData();
- const execSpy = sinon.spy( editor, 'execute' );
- setModelData( model, '<paragraph>x</paragraph><blockQuote><paragraph>[]</paragraph></blockQuote><paragraph>x</paragraph>' );
- viewDocument.fire( 'enter', data );
- expect( data.preventDefault.called ).to.be.true;
- expect( execSpy.calledOnce ).to.be.true;
- expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'blockQuote' );
- expect( getModelData( model ) ).to.equal( '<paragraph>x</paragraph><paragraph>[]</paragraph><paragraph>x</paragraph>' );
- } );
- it( 'escapes block quote if selection is in an empty block in the middle of a block quote', () => {
- const data = fakeEventData();
- const execSpy = sinon.spy( editor, 'execute' );
- setModelData( model,
- '<paragraph>x</paragraph>' +
- '<blockQuote><paragraph>a</paragraph><paragraph>[]</paragraph><paragraph>b</paragraph></blockQuote>' +
- '<paragraph>x</paragraph>'
- );
- viewDocument.fire( 'enter', data );
- expect( data.preventDefault.called ).to.be.true;
- expect( execSpy.calledOnce ).to.be.true;
- expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'blockQuote' );
- expect( getModelData( model ) ).to.equal(
- '<paragraph>x</paragraph>' +
- '<blockQuote><paragraph>a</paragraph></blockQuote>' +
- '<paragraph>[]</paragraph>' +
- '<blockQuote><paragraph>b</paragraph></blockQuote>' +
- '<paragraph>x</paragraph>'
- );
- } );
- it( 'escapes block quote if selection is in an empty block at the end of a block quote', () => {
- const data = fakeEventData();
- const execSpy = sinon.spy( editor, 'execute' );
- setModelData( model,
- '<paragraph>x</paragraph>' +
- '<blockQuote><paragraph>a</paragraph><paragraph>[]</paragraph></blockQuote>' +
- '<paragraph>x</paragraph>'
- );
- viewDocument.fire( 'enter', data );
- expect( data.preventDefault.called ).to.be.true;
- expect( execSpy.calledOnce ).to.be.true;
- expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'blockQuote' );
- expect( getModelData( model ) ).to.equal(
- '<paragraph>x</paragraph>' +
- '<blockQuote><paragraph>a</paragraph></blockQuote>' +
- '<paragraph>[]</paragraph>' +
- '<paragraph>x</paragraph>'
- );
- } );
- it( 'scrolls the view document to the selection after the command is executed', () => {
- const data = fakeEventData();
- const execSpy = sinon.spy( editor, 'execute' );
- const scrollSpy = sinon.stub( editor.editing.view, 'scrollToTheSelection' );
- setModelData( model,
- '<paragraph>x</paragraph>' +
- '<blockQuote><paragraph>a</paragraph><paragraph>[]</paragraph></blockQuote>' +
- '<paragraph>x</paragraph>'
- );
- viewDocument.fire( 'enter', data );
- sinon.assert.calledOnce( scrollSpy );
- sinon.assert.callOrder( execSpy, scrollSpy );
- } );
- } );
- describe( 'backspace key support', () => {
- function fakeEventData() {
- return {
- preventDefault: sinon.spy(),
- direction: 'backward',
- unit: 'character'
- };
- }
- it( 'merges paragraph into paragraph in the quote', () => {
- const data = fakeEventData();
- setModelData( model,
- '<blockQuote><paragraph>a</paragraph><paragraph>b</paragraph></blockQuote>' +
- '<paragraph>[]c</paragraph>' +
- '<paragraph>d</paragraph>'
- );
- viewDocument.fire( 'delete', data );
- expect( getModelData( model ) ).to.equal(
- '<blockQuote><paragraph>a</paragraph><paragraph>b[]c</paragraph></blockQuote>' +
- '<paragraph>d</paragraph>'
- );
- } );
- it( 'merges paragraph from a quote into a paragraph before quote', () => {
- const data = fakeEventData();
- setModelData( model,
- '<paragraph>x</paragraph>' +
- '<blockQuote><paragraph>[]a</paragraph><paragraph>b</paragraph></blockQuote>' +
- '<paragraph>y</paragraph>'
- );
- viewDocument.fire( 'delete', data );
- expect( getModelData( model ) ).to.equal(
- '<paragraph>x[]a</paragraph>' +
- '<blockQuote><paragraph>b</paragraph></blockQuote>' +
- '<paragraph>y</paragraph>'
- );
- } );
- it( 'merges two quotes', () => {
- const data = fakeEventData();
- setModelData( model,
- '<paragraph>x</paragraph>' +
- '<blockQuote><paragraph>a</paragraph><paragraph>b</paragraph></blockQuote>' +
- '<blockQuote><paragraph>[]c</paragraph><paragraph>d</paragraph></blockQuote>' +
- '<paragraph>y</paragraph>'
- );
- viewDocument.fire( 'delete', data );
- expect( getModelData( model ) ).to.equal(
- '<paragraph>x</paragraph>' +
- '<blockQuote><paragraph>a</paragraph><paragraph>b[]c</paragraph><paragraph>d</paragraph></blockQuote>' +
- '<paragraph>y</paragraph>'
- );
- } );
- it( 'removes empty quote when merging into another quote', () => {
- const data = fakeEventData();
- setModelData( model,
- '<paragraph>x</paragraph>' +
- '<blockQuote><paragraph>a</paragraph></blockQuote>' +
- '<blockQuote><paragraph>[]</paragraph></blockQuote>' +
- '<paragraph>y</paragraph>'
- );
- viewDocument.fire( 'delete', data );
- expect( getModelData( model ) ).to.equal(
- '<paragraph>x</paragraph>' +
- '<blockQuote><paragraph>a[]</paragraph></blockQuote>' +
- '<paragraph>y</paragraph>'
- );
- } );
- it( 'removes empty quote when merging into a paragraph', () => {
- const data = fakeEventData();
- setModelData( model,
- '<paragraph>x</paragraph>' +
- '<blockQuote><paragraph>[]</paragraph></blockQuote>' +
- '<paragraph>y</paragraph>'
- );
- viewDocument.fire( 'delete', data );
- expect( getModelData( model ) ).to.equal(
- '<paragraph>x[]</paragraph>' +
- '<paragraph>y</paragraph>'
- );
- } );
- } );
- // Historically, due to problems with schema, images were not quotable.
- // These tests were left here to confirm that after schema was fixed, images are properly quotable.
- describe( 'compatibility with images', () => {
- it( 'quotes a simple image', () => {
- const element = document.createElement( 'div' );
- document.body.appendChild( element );
- // We can't load ImageCaption in this test because it adds <caption> to all images automatically.
- return ClassicTestEditor
- .create( element, {
- plugins: [ BlockQuote, Paragraph, Image ]
- } )
- .then( editor => {
- setModelData( editor.model,
- '<paragraph>fo[o</paragraph>' +
- '<image src="foo.png"></image>' +
- '<paragraph>b]ar</paragraph>'
- );
- editor.execute( 'blockQuote' );
- expect( getModelData( editor.model ) ).to.equal(
- '<blockQuote>' +
- '<paragraph>fo[o</paragraph>' +
- '<image src="foo.png"></image>' +
- '<paragraph>b]ar</paragraph>' +
- '</blockQuote>'
- );
- element.remove();
- return editor.destroy();
- } );
- } );
- it( 'quotes an image with caption', () => {
- setModelData( model,
- '<paragraph>fo[o</paragraph>' +
- '<image src="foo.png">' +
- '<caption>xxx</caption>' +
- '</image>' +
- '<paragraph>b]ar</paragraph>'
- );
- editor.execute( 'blockQuote' );
- expect( getModelData( model ) ).to.equal(
- '<blockQuote>' +
- '<paragraph>fo[o</paragraph>' +
- '<image src="foo.png">' +
- '<caption>xxx</caption>' +
- '</image>' +
- '<paragraph>b]ar</paragraph>' +
- '</blockQuote>'
- );
- } );
- it( 'adds an image to an existing quote', () => {
- setModelData( model,
- '<paragraph>fo[o</paragraph>' +
- '<image src="foo.png">' +
- '<caption>xxx</caption>' +
- '</image>' +
- '<blockQuote><paragraph>b]ar</paragraph></blockQuote>'
- );
- editor.execute( 'blockQuote' );
- // Selection incorrectly trimmed.
- expect( getModelData( model ) ).to.equal(
- '<blockQuote>' +
- '<paragraph>foo</paragraph>' +
- '<image src="foo.png">' +
- '<caption>xxx</caption>' +
- '</image>' +
- '<paragraph>[b]ar</paragraph>' +
- '</blockQuote>'
- );
- } );
- } );
- // When blockQuote with a paragraph was pasted into a list item, the item contained the paragraph. It was invalid.
- // There is a test which checks whether blockQuote will split the list items instead of merging with.
- describe( 'compatibility with lists', () => {
- it( 'does not merge the paragraph with list item', () => {
- setModelData( model, '<listItem listIndent="0" listType="bulleted">fo[]o</listItem>' );
- const df = parseModel(
- '<blockQuote><paragraph>xxx</paragraph></blockQuote><heading1>yyy</heading1>',
- model.schema
- );
- model.insertContent( df, model.document.selection );
- expect( getModelData( model ) ).to.equal(
- '<listItem listIndent="0" listType="bulleted">fo</listItem>' +
- '<blockQuote>' +
- '<paragraph>xxx</paragraph>' +
- '</blockQuote>' +
- '<heading1>yyy[]o</heading1>'
- );
- } );
- } );
- } );
|