/** * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ import BlockQuoteEditing from '../src/blockquoteediting.js'; import BlockQuoteCommand from '../src/blockquotecommand.js'; import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor.js'; import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model.js'; import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view.js'; import Command from '@ckeditor/ckeditor5-core/src/command.js'; describe( 'BlockQuoteCommand', () => { let editor, model, command; beforeEach( () => { return VirtualTestEditor .create( { plugins: [ BlockQuoteEditing ] } ) .then( newEditor => { editor = newEditor; model = editor.model; model.schema.register( 'paragraph', { inheritAllFrom: '$block' } ); model.schema.register( 'heading', { inheritAllFrom: '$block' } ); model.schema.register( 'widget' ); model.schema.extend( 'widget', { allowIn: '$root', allowChildren: '$text', isLimit: true, isObject: true } ); editor.conversion.for( 'downcast' ).elementToElement( { model: 'paragraph', view: 'p' } ); editor.conversion.for( 'downcast' ).elementToElement( { model: 'heading', view: 'h' } ); editor.conversion.for( 'downcast' ).elementToElement( { model: 'widget', view: 'widget' } ); command = editor.commands.get( 'blockQuote' ); } ); } ); afterEach( async () => { await editor.destroy(); } ); it( 'is a command', () => { expect( BlockQuoteCommand.prototype ).to.be.instanceOf( Command ); expect( command ).to.be.instanceOf( Command ); } ); describe( 'value', () => { it( 'is false when selection is not in a block quote', () => { setModelData( model, 'x[]x' ); expect( command ).to.have.property( 'value', false ); } ); it( 'is false when start of the selection is not in a block quote', () => { setModelData( model, 'x[x
y]y
' ); expect( command ).to.have.property( 'value', false ); } ); it( 'is false when selection starts in a blockless space', () => { model.schema.extend( '$text', { allowIn: '$root' } ); setModelData( model, 'x[]x' ); expect( command ).to.have.property( 'value', false ); } ); it( 'is true when selection is in a block quote', () => { setModelData( model, '
x[]x
' ); expect( command ).to.have.property( 'value', true ); } ); it( 'is true when selection starts in a block quote', () => { setModelData( model, '
x[x
y]y' ); expect( command ).to.have.property( 'value', true ); } ); } ); describe( 'isEnabled', () => { it( 'is true when selection is in a block which can be wrapped with blockQuote', () => { setModelData( model, 'x[]x' ); expect( command ).to.have.property( 'isEnabled', true ); } ); it( 'is true when selection is in a block which is already in blockQuote', () => { setModelData( model, '
x[]x
' ); expect( command ).to.have.property( 'isEnabled', true ); } ); it( 'is true when selection starts in a block which can be wrapped with blockQuote', () => { setModelData( model, 'x[xy]y' ); expect( command ).to.have.property( 'isEnabled', true ); } ); it( 'is false when selection is in an element which cannot be wrapped with blockQuote (because it cannot be its child)', () => { setModelData( model, 'x[]x' ); expect( command ).to.have.property( 'isEnabled', false ); } ); it( 'is false when selection is in an element which cannot be wrapped with blockQuote' + '(because mQ is not allowed in its parent)', () => { model.schema.addChildCheck( ( ctx, childDef ) => { if ( childDef.name == 'blockQuote' ) { return false; } } ); setModelData( model, 'x[]x' ); expect( command ).to.have.property( 'isEnabled', false ); } ); // https://github.com/ckeditor/ckeditor5-engine/issues/826 // it( 'is false when selection starts in an element which cannot be wrapped with blockQuote', () => { // setModelData( model, 'x[xy]y' ); // expect( command ).to.have.property( 'isEnabled', false ); // } ); } ); describe( 'execute()', () => { describe( 'applying quote', () => { it( 'should wrap a single block', () => { setModelData( model, 'abc' + 'x[]x' + 'def' ); editor.execute( 'blockQuote' ); expect( getModelData( model ) ).to.equal( 'abc' + '
x[]x
' + 'def' ); expect( getViewData( editor.editing.view ) ).to.equal( '

abc

x{}x

def

' ); } ); it( 'should wrap multiple blocks', () => { setModelData( model, 'a[bc' + 'xx' + 'de]f' ); editor.execute( 'blockQuote' ); expect( getModelData( model ) ).to.equal( '
' + 'a[bc' + 'xx' + 'de]f' + '
' ); expect( getViewData( editor.editing.view ) ).to.equal( '
a{bc

xx

de}f

' ); } ); it( 'should merge with an existing quote', () => { setModelData( model, 'a[bc' + '
x]xyy
' + 'def' ); editor.execute( 'blockQuote' ); // Selection incorrectly trimmed. expect( getModelData( model ) ).to.equal( '
' + 'abc' + '[x]x' + 'yy' + '
' + 'def' ); // Selection incorrectly trimmed. expect( getViewData( editor.editing.view ) ).to.equal( '
abc

{x}x

yy

def

' ); } ); it( 'should not merge with a quote preceding the current block', () => { setModelData( model, '
abc
' + 'x[]x' ); editor.execute( 'blockQuote' ); expect( getModelData( model ) ).to.equal( '
abc
' + '
x[]x
' ); expect( getViewData( editor.editing.view ) ).to.equal( '

abc

' + '

x{}x

' ); } ); it( 'should not merge with a quote following the current block', () => { setModelData( model, 'x[]x' + '
abc
' ); editor.execute( 'blockQuote' ); expect( getModelData( model ) ).to.equal( '
x[]x
' + '
abc
' ); expect( getViewData( editor.editing.view ) ).to.equal( '

x{}x

' + '

abc

' ); } ); it( 'should merge with an existing quote (more blocks)', () => { setModelData( model, 'a[bc' + 'def' + '
x]x
' + 'ghi' ); editor.execute( 'blockQuote' ); // Selection incorrectly trimmed. expect( getModelData( model ) ).to.equal( '
' + 'abc' + 'def' + '[x]x' + '
' + 'ghi' ); // Selection incorrectly trimmed. expect( getViewData( editor.editing.view ) ).to.equal( '
abc

def

{x}x

ghi

' ); } ); it( 'should not wrap non-block content', () => { setModelData( model, 'a[bc' + 'xx' + 'de]f' ); editor.execute( 'blockQuote' ); // Selection incorrectly trimmed. expect( getModelData( model ) ).to.equal( '
' + 'abc' + '
' + '[xx' + '
' + 'de]f' + '
' ); // Selection incorrectly trimmed. expect( getViewData( editor.editing.view ) ).to.equal( '

abc

[xx

de}f

' ); } ); it( 'should correctly wrap and merge groups of blocks', () => { setModelData( model, 'a[bc' + 'xx' + 'def' + '
ghi
' + 'yy' + 'jk]l' ); editor.execute( 'blockQuote' ); // Selection incorrectly trimmed. expect( getModelData( model ) ).to.equal( '
abc
' + '[xx' + '
defghi
' + 'yy' + '
jk]l
' ); // Selection incorrectly trimmed. expect( getViewData( editor.editing.view ) ).to.equal( '

abc

' + '[xx' + '

def

ghi

' + 'yy' + '

jk}l

' ); } ); it( 'should correctly merge a couple of subsequent quotes', () => { setModelData( model, 'x' + 'a[bc' + '
def
' + 'ghi' + '
jkl
' + 'mn]o' + 'y' ); editor.execute( 'blockQuote' ); // Selection incorrectly trimmed. expect( getModelData( model ) ).to.equal( 'x' + '
' + 'abc' + 'def' + 'ghi' + 'jkl' + '[mn]o' + '
' + 'y' ); // Selection incorrectly trimmed. expect( getViewData( editor.editing.view ) ).to.equal( '

x

' + '
' + '

abc

' + '

def

' + '

ghi

' + '

jkl

' + '

{mn}o

' + '
' + '

y

' ); } ); it( 'should not wrap a block which can not be in a quote', () => { // blockQuote is allowed in root, but fooBlock can not be inside blockQuote. model.schema.register( 'fooBlock', { inheritAllFrom: '$block' } ); model.schema.addChildCheck( ( ctx, childDef ) => { if ( ctx.endsWith( 'blockQuote' ) && childDef.name == 'fooBlock' ) { return false; } } ); editor.conversion.for( 'downcast' ).elementToElement( { model: 'fooBlock', view: 'fooblock' } ); setModelData( model, 'a[bc' + 'xx' + 'de]f' ); editor.execute( 'blockQuote' ); // Selection incorrectly trimmed. expect( getModelData( model ) ).to.equal( '
' + 'abc' + '
' + '[xx' + '
' + 'de]f' + '
' ); } ); it( 'should not wrap a block which parent does not allow quote inside itself', () => { // blockQuote is not be allowed in fooWrapper, but fooBlock can be inside blockQuote. model.schema.register( 'fooWrapper' ); model.schema.register( 'fooBlock', { inheritAllFrom: '$block' } ); model.schema.extend( 'fooWrapper', { allowIn: '$root' } ); model.schema.extend( 'fooBlock', { allowIn: 'fooWrapper' } ); editor.conversion.for( 'downcast' ).elementToElement( { model: 'fooWrapper', view: 'foowrapper' } ); editor.conversion.for( 'downcast' ).elementToElement( { model: 'fooBlock', view: 'fooblock' } ); setModelData( model, 'a[bc' + 'xx' + 'de]f' ); editor.execute( 'blockQuote' ); // Selection incorrectly trimmed. expect( getModelData( model ) ).to.equal( '
' + 'abc' + '
' + '[xx' + '
' + 'de]f' + '
' ); } ); it( 'should handle forceValue = true param', () => { setModelData( model, '
' + 'x[x' + '
' + 'd]ef' ); editor.execute( 'blockQuote', { forceValue: true } ); expect( getModelData( model ) ).to.equal( '
' + 'x[x' + 'd]ef' + '
' ); expect( getViewData( editor.editing.view ) ).to.equal( '

x{x

d}ef

' ); } ); } ); describe( 'removing quote', () => { it( 'should unwrap a single block', () => { setModelData( model, 'abc' + '
x[]x
' + 'def' ); editor.execute( 'blockQuote' ); expect( getModelData( model ) ).to.equal( 'abc' + 'x[]x' + 'def' ); expect( getViewData( editor.editing.view ) ).to.equal( '

abc

x{}x

def

' ); } ); it( 'should unwrap multiple blocks', () => { setModelData( model, '
' + 'a[bc' + 'xx' + 'de]f' + '
' ); editor.execute( 'blockQuote' ); expect( getModelData( model ) ).to.equal( 'a[bc' + 'xx' + 'de]f' ); expect( getViewData( editor.editing.view ) ).to.equal( '

a{bc

xx

de}f

' ); } ); it( 'should unwrap only the selected blocks - at the beginning', () => { setModelData( model, 'xx' + '
' + 'a[b]c' + 'xx' + '
' + 'yy' ); editor.execute( 'blockQuote' ); expect( getModelData( model ) ).to.equal( 'xx' + 'a[b]c' + '
' + 'xx' + '
' + 'yy' ); expect( getViewData( editor.editing.view ) ).to.equal( '

xx

a{b}c

xx

yy

' ); } ); it( 'should unwrap only the selected blocks - at the end', () => { setModelData( model, '
' + 'abc' + 'x[x' + '
' + 'de]f' ); editor.execute( 'blockQuote' ); expect( getModelData( model ) ).to.equal( '
' + 'abc' + '
' + 'x[x' + 'de]f' ); expect( getViewData( editor.editing.view ) ).to.equal( '

abc

x{x

de}f

' ); } ); it( 'should unwrap only the selected blocks - in the middle', () => { setModelData( model, 'xx' + '
' + 'abc' + 'c[]de' + 'fgh' + '
' + 'xx' ); editor.execute( 'blockQuote' ); expect( getModelData( model ) ).to.equal( 'xx' + '
abc
' + 'c[]de' + '
fgh
' + 'xx' ); expect( getViewData( editor.editing.view ) ).to.equal( '

xx

' + '

abc

' + '

c{}de

' + '

fgh

' + '

xx

' ); } ); it( 'should remove multiple quotes', () => { setModelData( model, '
a[bc
' + 'xx' + '
defghi
' + 'yy' + '
de]fghi
' ); editor.execute( 'blockQuote' ); expect( getModelData( model ) ).to.equal( 'a[bc' + 'xx' + 'defghi' + 'yy' + 'de]f' + '
ghi
' ); expect( getViewData( editor.editing.view ) ).to.equal( '

a{bc

' + '

xx

' + '

def

ghi

' + '

yy

' + '

de}f

' + '

ghi

' ); } ); it( 'should handle forceValue = false param', () => { setModelData( model, 'a[bc' + '
' + 'x]x' + '
' ); editor.execute( 'blockQuote', { forceValue: false } ); // Incorrect selection. expect( getModelData( model ) ).to.equal( 'a[bc]' + 'xx' ); expect( getViewData( editor.editing.view ) ).to.equal( '

a{bc}

xx

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