Best JavaScript code snippet using cypress
listediting.js
Source:listediting.js
1/**2 * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.3 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license4 */5import ListEditing from '../src/listediting';6import ListCommand from '../src/listcommand';7import IndentCommand from '../src/indentcommand';8import ModelRange from '@ckeditor/ckeditor5-engine/src/model/range';9import BoldEditing from '@ckeditor/ckeditor5-basic-styles/src/bold/boldediting';10import UndoEditing from '@ckeditor/ckeditor5-undo/src/undoediting';11import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';12import BlockQuoteEditing from '@ckeditor/ckeditor5-block-quote/src/blockquoteediting';13import HeadingEditing from '@ckeditor/ckeditor5-heading/src/headingediting';14import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';15import { getData as getModelData, parse as parseModel, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';16import { getData as getViewData, parse as parseView } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';17import IndentEditing from '@ckeditor/ckeditor5-indent/src/indentediting';18import { getCode } from '@ckeditor/ckeditor5-utils/src/keyboard';19import { assertEqualMarkup } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';20import TableEditing from '@ckeditor/ckeditor5-table/src/tableediting';21describe( 'ListEditing', () => {22 let editor, model, modelDoc, modelRoot, view, viewDoc, viewRoot;23 beforeEach( () => {24 return VirtualTestEditor25 .create( {26 plugins: [ Clipboard, BoldEditing, ListEditing, UndoEditing, BlockQuoteEditing, TableEditing ]27 } )28 .then( newEditor => {29 editor = newEditor;30 model = editor.model;31 modelDoc = model.document;32 modelRoot = modelDoc.getRoot();33 view = editor.editing.view;34 viewDoc = view.document;35 viewRoot = viewDoc.getRoot();36 model.schema.register( 'foo', {37 allowWhere: '$block',38 allowAttributes: [ 'listIndent', 'listType' ],39 isBlock: true,40 isObject: true41 } );42 } );43 } );44 afterEach( () => {45 return editor.destroy();46 } );47 it( 'should have pluginName', () => {48 expect( ListEditing.pluginName ).to.equal( 'ListEditing' );49 } );50 it( 'should be loaded', () => {51 expect( editor.plugins.get( ListEditing ) ).to.be.instanceOf( ListEditing );52 } );53 it( 'should set proper schema rules', () => {54 expect( model.schema.isRegistered( 'listItem' ) );55 expect( model.schema.isBlock( 'listItem' ) );56 expect( model.schema.checkChild( [ '$root' ], 'listItem' ) ).to.be.true;57 expect( model.schema.checkChild( [ '$root', 'listItem' ], '$text' ) ).to.be.true;58 expect( model.schema.checkChild( [ '$root', 'listItem' ], 'listItem' ) ).to.be.false;59 expect( model.schema.checkChild( [ '$root', 'listItem' ], '$block' ) ).to.be.false;60 expect( model.schema.checkAttribute( [ '$root', 'listItem' ], 'listIndent' ) ).to.be.true;61 expect( model.schema.checkAttribute( [ '$root', 'listItem' ], 'listType' ) ).to.be.true;62 } );63 describe( 'commands', () => {64 it( 'should register bulleted list command', () => {65 const command = editor.commands.get( 'bulletedList' );66 expect( command ).to.be.instanceOf( ListCommand );67 expect( command ).to.have.property( 'type', 'bulleted' );68 } );69 it( 'should register numbered list command', () => {70 const command = editor.commands.get( 'numberedList' );71 expect( command ).to.be.instanceOf( ListCommand );72 expect( command ).to.have.property( 'type', 'numbered' );73 } );74 it( 'should register indent list command', () => {75 const command = editor.commands.get( 'indentList' );76 expect( command ).to.be.instanceOf( IndentCommand );77 } );78 it( 'should register outdent list command', () => {79 const command = editor.commands.get( 'outdentList' );80 expect( command ).to.be.instanceOf( IndentCommand );81 } );82 it( 'should add indent list command to indent command', () => {83 return VirtualTestEditor84 .create( {85 plugins: [ ListEditing, IndentEditing ]86 } )87 .then( newEditor => {88 editor = newEditor;89 } )90 .then( () => {91 const indentListCommand = editor.commands.get( 'indentList' );92 const indentCommand = editor.commands.get( 'indent' );93 const spy = sinon.spy( indentListCommand, 'execute' );94 indentListCommand.isEnabled = true;95 indentCommand.execute();96 sinon.assert.calledOnce( spy );97 } );98 } );99 it( 'should add outdent list command to outdent command', () => {100 return VirtualTestEditor101 .create( {102 plugins: [ ListEditing, IndentEditing ]103 } )104 .then( newEditor => {105 editor = newEditor;106 } )107 .then( () => {108 const outdentListCommand = editor.commands.get( 'outdentList' );109 const outdentCommand = editor.commands.get( 'outdent' );110 const spy = sinon.spy( outdentListCommand, 'execute' );111 outdentListCommand.isEnabled = true;112 outdentCommand.execute();113 sinon.assert.calledOnce( spy );114 } );115 } );116 } );117 describe( 'enter key handling callback', () => {118 it( 'should execute outdentList command on enter key in empty list', () => {119 const domEvtDataStub = { preventDefault() {} };120 sinon.spy( editor, 'execute' );121 setModelData( model, '<listItem listType="bulleted" listIndent="0">[]</listItem>' );122 editor.editing.view.document.fire( 'enter', domEvtDataStub );123 sinon.assert.calledOnce( editor.execute );124 sinon.assert.calledWithExactly( editor.execute, 'outdentList' );125 } );126 it( 'should not execute outdentList command on enter key in non-empty list', () => {127 const domEvtDataStub = { preventDefault() {} };128 sinon.spy( editor, 'execute' );129 setModelData( model, '<listItem listType="bulleted" listIndent="0">foo[]</listItem>' );130 editor.editing.view.document.fire( 'enter', domEvtDataStub );131 sinon.assert.notCalled( editor.execute );132 } );133 } );134 describe( 'delete key handling callback', () => {135 it( 'should execute outdentList command on backspace key in first item of list (first node in root)', () => {136 const domEvtDataStub = { preventDefault() {}, direction: 'backward' };137 sinon.spy( editor, 'execute' );138 setModelData( model, '<listItem listType="bulleted" listIndent="0">[]foo</listItem>' );139 editor.editing.view.document.fire( 'delete', domEvtDataStub );140 sinon.assert.calledWithExactly( editor.execute, 'outdentList' );141 } );142 it( 'should execute outdentList command on backspace key in first item of list (after a paragraph)', () => {143 const domEvtDataStub = { preventDefault() {}, direction: 'backward' };144 sinon.spy( editor, 'execute' );145 setModelData( model, '<paragraph>foo</paragraph><listItem listType="bulleted" listIndent="0">[]foo</listItem>' );146 editor.editing.view.document.fire( 'delete', domEvtDataStub );147 sinon.assert.calledWithExactly( editor.execute, 'outdentList' );148 } );149 it( 'should not execute outdentList command on delete key in first item of list', () => {150 const domEvtDataStub = { preventDefault() {}, direction: 'forward' };151 sinon.spy( editor, 'execute' );152 setModelData( model, '<listItem listType="bulleted" listIndent="0">[]foo</listItem>' );153 editor.editing.view.document.fire( 'delete', domEvtDataStub );154 sinon.assert.notCalled( editor.execute );155 } );156 it( 'should not execute outdentList command when selection is not collapsed', () => {157 const domEvtDataStub = { preventDefault() {}, direction: 'backward' };158 sinon.spy( editor, 'execute' );159 setModelData( model, '<listItem listType="bulleted" listIndent="0">[fo]o</listItem>' );160 editor.editing.view.document.fire( 'delete', domEvtDataStub );161 sinon.assert.notCalled( editor.execute );162 } );163 it( 'should not execute outdentList command if not in list item', () => {164 const domEvtDataStub = { preventDefault() {}, direction: 'backward' };165 sinon.spy( editor, 'execute' );166 setModelData( model, '<paragraph>[]foo</paragraph>' );167 editor.editing.view.document.fire( 'delete', domEvtDataStub );168 sinon.assert.notCalled( editor.execute );169 } );170 it( 'should not execute outdentList command if not in first list item', () => {171 const domEvtDataStub = { preventDefault() {}, direction: 'backward' };172 sinon.spy( editor, 'execute' );173 setModelData(174 model,175 '<listItem listType="bulleted" listIndent="0">foo</listItem><listItem listType="bulleted" listIndent="0">[]foo</listItem>'176 );177 editor.editing.view.document.fire( 'delete', domEvtDataStub );178 sinon.assert.notCalled( editor.execute );179 } );180 it( 'should not execute outdentList command when selection is not on first position', () => {181 const domEvtDataStub = { preventDefault() {}, direction: 'backward' };182 sinon.spy( editor, 'execute' );183 setModelData( model, '<listItem listType="bulleted" listIndent="0">fo[]o</listItem>' );184 editor.editing.view.document.fire( 'delete', domEvtDataStub );185 sinon.assert.notCalled( editor.execute );186 } );187 it( 'should outdent list when previous element is nested in block quote', () => {188 const domEvtDataStub = { preventDefault() {}, direction: 'backward' };189 sinon.spy( editor, 'execute' );190 setModelData(191 model,192 '<blockQuote><paragraph>x</paragraph></blockQuote><listItem listType="bulleted" listIndent="0">[]foo</listItem>'193 );194 editor.editing.view.document.fire( 'delete', domEvtDataStub );195 sinon.assert.calledWithExactly( editor.execute, 'outdentList' );196 } );197 it( 'should outdent list when list is nested in block quote', () => {198 const domEvtDataStub = { preventDefault() {}, direction: 'backward' };199 sinon.spy( editor, 'execute' );200 setModelData(201 model,202 '<paragraph>x</paragraph><blockQuote><listItem listType="bulleted" listIndent="0">[]foo</listItem></blockQuote>'203 );204 editor.editing.view.document.fire( 'delete', domEvtDataStub );205 sinon.assert.calledWithExactly( editor.execute, 'outdentList' );206 } );207 } );208 describe( 'tab key handling callback', () => {209 let domEvtDataStub;210 beforeEach( () => {211 domEvtDataStub = {212 keyCode: getCode( 'Tab' ),213 preventDefault: sinon.spy(),214 stopPropagation: sinon.spy()215 };216 sinon.spy( editor, 'execute' );217 } );218 afterEach( () => {219 editor.execute.restore();220 } );221 it( 'should execute indentList command on tab key', () => {222 setModelData(223 model,224 '<listItem listType="bulleted" listIndent="0">foo</listItem>' +225 '<listItem listType="bulleted" listIndent="0">[]bar</listItem>'226 );227 editor.editing.view.document.fire( 'keydown', domEvtDataStub );228 sinon.assert.calledOnce( editor.execute );229 sinon.assert.calledWithExactly( editor.execute, 'indentList' );230 sinon.assert.calledOnce( domEvtDataStub.preventDefault );231 sinon.assert.calledOnce( domEvtDataStub.stopPropagation );232 } );233 it( 'should execute outdentList command on Shift+Tab keystroke', () => {234 domEvtDataStub.keyCode += getCode( 'Shift' );235 setModelData(236 model,237 '<listItem listType="bulleted" listIndent="0">foo</listItem>' +238 '<listItem listType="bulleted" listIndent="1">[]bar</listItem>'239 );240 editor.editing.view.document.fire( 'keydown', domEvtDataStub );241 sinon.assert.calledOnce( editor.execute );242 sinon.assert.calledWithExactly( editor.execute, 'outdentList' );243 sinon.assert.calledOnce( domEvtDataStub.preventDefault );244 sinon.assert.calledOnce( domEvtDataStub.stopPropagation );245 } );246 it( 'should not indent if command is disabled', () => {247 setModelData( model, '<listItem listType="bulleted" listIndent="0">[]foo</listItem>' );248 editor.editing.view.document.fire( 'keydown', domEvtDataStub );249 expect( editor.execute.called ).to.be.false;250 sinon.assert.notCalled( domEvtDataStub.preventDefault );251 sinon.assert.notCalled( domEvtDataStub.stopPropagation );252 } );253 it( 'should not indent or outdent if alt+tab is pressed', () => {254 domEvtDataStub.keyCode += getCode( 'alt' );255 setModelData(256 model,257 '<listItem listType="bulleted" listIndent="0">foo</listItem>' +258 '<listItem listType="bulleted" listIndent="0">[]bar</listItem>'259 );260 editor.editing.view.document.fire( 'keydown', domEvtDataStub );261 expect( editor.execute.called ).to.be.false;262 sinon.assert.notCalled( domEvtDataStub.preventDefault );263 sinon.assert.notCalled( domEvtDataStub.stopPropagation );264 } );265 } );266 describe( 'flat lists', () => {267 describe( 'setting data', () => {268 function testList( testName, string, expectedString = null ) {269 it( testName, () => {270 editor.setData( string );271 expect( editor.getData() ).to.equal( expectedString || string );272 } );273 }274 testList( 'single item', '<ul><li>x</li></ul>' );275 testList( 'multiple items', '<ul><li>a</li><li>b</li><li>c</li></ul>' );276 testList( 'items and text', '<p>xxx</p><ul><li>a</li><li>b</li></ul><p>yyy</p><ul><li>c</li><li>d</li></ul>' );277 testList( 'numbered list', '<ol><li>a</li><li>b</li></ol>' );278 testList( 'mixed list and content #1', '<p>xxx</p><ul><li>a</li><li>b</li></ul><ol><li>c</li><li>d</li></ol><p>yyy</p>' );279 testList( 'mixed list and content #2',280 '<ol><li>a</li></ol><p>xxx</p><ul><li>b</li><li>c</li></ul><p>yyy</p><ul><li>d</li></ul>' );281 testList(282 'clears incorrect elements',283 '<ul>x<li>a</li><li>b</li><p>xxx</p>x</ul><p>c</p>', '<ul><li>a</li><li>b</li></ul><p>c</p>'284 );285 testList(286 'clears whitespaces',287 '<p>foo</p>' +288 '<ul>' +289 ' <li>xxx</li>' +290 ' <li>yyy</li>' +291 '</ul>',292 '<p>foo</p><ul><li>xxx</li><li>yyy</li></ul>'293 );294 // #ckeditor5/1399295 testList( 'single item with `font-weight` style',296 '<ol><li style="font-weight: bold">foo</li></ol>', '<ol><li><strong>foo</strong></li></ol>' );297 it( 'model test for mixed content', () => {298 editor.setData( '<ol><li>a</li></ol><p>xxx</p><ul><li>b</li><li>c</li></ul><p>yyy</p><ul><li>d</li></ul>' );299 const expectedModelData =300 '<listItem listIndent="0" listType="numbered">a</listItem>' +301 '<paragraph>xxx</paragraph>' +302 '<listItem listIndent="0" listType="bulleted">b</listItem>' +303 '<listItem listIndent="0" listType="bulleted">c</listItem>' +304 '<paragraph>yyy</paragraph>' +305 '<listItem listIndent="0" listType="bulleted">d</listItem>';306 expect( getModelData( model, { withoutSelection: true } ) ).to.equal( expectedModelData );307 } );308 describe( 'block elements inside list items', () => {309 describe( 'single block', () => {310 testList(311 'single item',312 '<ul><li><p>Foo</p></li></ul>',313 '<p>Foo</p>'314 );315 testList(316 'multiple items',317 '<ul><li><p>Foo</p></li><li><p>Bar</p></li></ul>',318 '<p>Foo</p><p>Bar</p>'319 );320 testList(321 'nested items',322 '<ul><li><p>Foo</p><ol><li><p>Bar</p></li></ol></li></ul>',323 '<p>Foo</p><p>Bar</p>'324 );325 } );326 describe( 'multiple blocks', () => {327 testList(328 'single item',329 '<ul><li><h2>Foo</h2><p>Bar</p></li></ul>',330 '<p>Foo</p><p>Bar</p>'331 );332 testList(333 'multiple items',334 '<ol><li><p>123</p></li></ol><ul><li><h2>Foo</h2><p>Bar</p></li></ul>',335 '<p>123</p><p>Foo</p><p>Bar</p>'336 );337 testList(338 'nested items #2',339 '<ol><li><p>123</p><p>456</p><ul><li><h2>Foo</h2><p>Bar</p></li></ul></li></ol>',340 '<p>123</p><p>456</p><p>Foo</p><p>Bar</p>'341 );342 } );343 describe.skip( 'multiple blocks', () => { // Skip due to #112 issue.344 testList(345 'nested items #1',346 '<ol><li><p>123</p><ul><li><h2>Foo</h2><p>Bar</p></li></ul><p>456</p></li></ol>',347 '<p>123</p><p>Foo</p><p>Bar</p><p>456</p>'348 );349 } );350 describe( 'inline + block', () => {351 testList(352 'single item',353 '<ul><li>Foo<p>Bar</p></li></ul>',354 '<ul><li>Foo</li></ul><p>Bar</p>'355 );356 testList(357 'multiple items',358 '<ul><li>Foo<p>Bar</p></li><li>Foz<p>Baz</p></li></ul>',359 '<ul><li>Foo</li></ul><p>Bar</p><ul><li>Foz</li></ul><p>Baz</p>'360 );361 testList(362 'split by list items',363 '<ul><li>Foo</li><li><p>Bar</p></li></ul>',364 '<ul><li>Foo</li></ul><p>Bar</p>'365 );366 testList(367 'nested split by list items',368 '<ul><li>Foo<ol><li><p>Bar</p></li></ol></li></ul>',369 '<ul><li>Foo</li></ul><p>Bar</p>'370 );371 testList(372 'nested items #1',373 '<ol><li>Foo<p>Bar</p><ul><li>123<h2>456</h2></li></ul></li></ol>',374 '<ol><li>Foo</li></ol><p>Bar</p><ul><li>123</li></ul><p>456</p>'375 );376 testList(377 'nested items #2',378 '<ol><li>Foo<p>Bar</p><ul><li>123<h2>456</h2></li></ul></li><li>abc<h2>def</h2></li></ol>',379 '<ol><li>Foo</li></ol><p>Bar</p><ul><li>123</li></ul><p>456</p><ol><li>abc</li></ol><p>def</p>'380 );381 } );382 describe( 'block + inline', () => {383 testList(384 'single item',385 '<ul><li><p>Foo</p>Bar</li></ul>',386 '<p>Foo</p><ul><li>Bar</li></ul>'387 );388 testList(389 'multiple items',390 '<ul><li><p>Foo</p>Bar</li><li><p>Foz</p>Baz</li></ul>',391 '<p>Foo</p><ul><li>Bar</li></ul><p>Foz</p><ul><li>Baz</li></ul>'392 );393 testList(394 'split by list items',395 '<ul><li><p>Bar</p><li>Foo</li></li></ul>',396 '<p>Bar</p><ul><li>Foo</li></ul>'397 );398 testList(399 'nested split by list items',400 '<ul><li><p>Bar</p><ol><li>Foo</li></ol></li></ul>',401 '<p>Bar</p><ol><li>Foo</li></ol>'402 );403 testList(404 'nested items #1',405 '<ol><li><p>Foo</p>Bar<ul><li><h2>123</h2>456</li></ul></li></ol>',406 '<p>Foo</p><ol><li>Bar</li></ol><p>123</p><ul><li>456</li></ul>'407 );408 testList(409 'nested items #2',410 '<ol><li><p>Foo</p>Bar<ul><li><h2>123</h2>456</li></ul></li><li><h2>abc</h2>def</li></ol>',411 '<p>Foo</p><ol><li>Bar</li></ol><p>123</p><ul><li>456</li></ul><p>abc</p><ol><li>def</li></ol>'412 );413 } );414 describe( 'complex', () => {415 testList(416 'single item with inline block inline',417 '<ul><li>Foo<p>Bar</p>Baz</li></ul>',418 '<ul><li>Foo</li></ul><p>Bar</p><ul><li>Baz</li></ul>'419 );420 testList(421 'single item with inline block block',422 '<ul><li>Text<p>Foo</p><p>Bar</p></li></ul>',423 '<ul><li>Text</li></ul><p>Foo</p><p>Bar</p>'424 );425 testList(426 'single item with block block inline',427 '<ul><li><p>Foo</p><p>Bar</p>Text</li></ul>',428 '<p>Foo</p><p>Bar</p><ul><li>Text</li></ul>'429 );430 testList(431 'single item with block block block',432 '<ul><li><p>Foo</p><p>Bar</p><p>Baz</p></li></ul>',433 '<p>Foo</p><p>Bar</p><p>Baz</p>'434 );435 testList(436 'item inline + item block and inline',437 '<ul><li>Foo</li><li><p>Bar</p>Baz</li></ul>',438 '<ul><li>Foo</li></ul><p>Bar</p><ul><li>Baz</li></ul>'439 );440 testList(441 'item inline and block + item inline',442 '<ul><li>Foo<p>Bar</p></li><li>Baz</li></ul>',443 '<ul><li>Foo</li></ul><p>Bar</p><ul><li>Baz</li></ul>'444 );445 testList(446 'multiple items inline/block mix',447 '<ul><li>Text<p>Foo</p></li><li>Bar<p>Baz</p>123</li></ul>',448 '<ul><li>Text</li></ul><p>Foo</p><ul><li>Bar</li></ul><p>Baz</p><ul><li>123</li></ul>'449 );450 testList(451 'nested items',452 '<ul><li>Foo<p>Bar</p></li><li>Baz<p>123</p>456<ol><li>ABC<p>DEF</p></li><li>GHI</li></ol></li></ul>',453 '<ul><li>Foo</li></ul><p>Bar</p><ul><li>Baz</li></ul><p>123</p><ul><li>456<ol><li>ABC</li></ol></li></ul>' +454 '<p>DEF</p><ol><li>GHI</li></ol>'455 );456 testList(457 'list with empty inline element',458 '<ul><li><span></span>Foo<p>Bar</p></li></ul>',459 '<ul><li>Foo</li></ul><p>Bar</p>'460 );461 } );462 } );463 } );464 describe( 'position mapping', () => {465 let mapper;466 beforeEach( () => {467 mapper = editor.editing.mapper;468 editor.setData(469 '<p>a</p>' +470 '<ul>' +471 '<li>b</li>' +472 '<li>c</li>' +473 '<li>d</li>' +474 '</ul>' +475 '<p>e</p>' +476 '<ol>' +477 '<li>f</li>' +478 '</ol>' +479 '<p>g</p>'480 );481 } );482 /*483 <paragraph>a</paragraph>484 <listItem listIndent=0 listType="bulleted">b</listItem>485 <listItem listIndent=0 listType="bulleted">c</listItem>486 <listItem listIndent=0 listType="bulleted">d</listItem>487 <paragraph>e</paragraph>488 <listItem listIndent=0 listType="numbered">f</listItem>489 <paragraph>g</paragraph>490 */491 describe( 'view to model', () => {492 function testList( testName, viewPath, modelPath ) {493 it( testName, () => {494 const viewPos = getViewPosition( viewRoot, viewPath, view );495 const modelPos = mapper.toModelPosition( viewPos );496 expect( modelPos.root ).to.equal( modelRoot );497 expect( modelPos.path ).to.deep.equal( modelPath );498 } );499 }500 testList( 'before ul', [ 1 ], [ 1 ] ); // --> before first `listItem`501 testList( 'before first li', [ 1, 0 ], [ 1 ] ); // --> before first `listItem`502 testList( 'beginning of li', [ 1, 0, 0 ], [ 1, 0 ] ); // --> beginning of first `listItem`503 testList( 'end of li', [ 1, 0, 1 ], [ 1, 1 ] ); // --> end of first `listItem`504 testList( 'before middle li', [ 1, 1 ], [ 2 ] ); // --> before middle `listItem`505 testList( 'before last li', [ 1, 2 ], [ 3 ] ); // --> before last `listItem`506 testList( 'after last li', [ 1, 3 ], [ 4 ] ); // --> after last `listItem` / before `paragraph`507 testList( 'after ul', [ 2 ], [ 4 ] ); // --> after last `listItem` / before `paragraph`508 testList( 'before ol', [ 3 ], [ 5 ] ); // --> before numbered `listItem`509 testList( 'before only li', [ 3, 0 ], [ 5 ] ); // --> before numbered `listItem`510 testList( 'after only li', [ 3, 1 ], [ 6 ] ); // --> after numbered `listItem`511 testList( 'after ol', [ 4 ], [ 6 ] ); // --> after numbered `listItem`512 } );513 describe( 'model to view', () => {514 function testList( testName, modelPath, viewPath ) {515 it( testName, () => {516 const modelPos = model.createPositionFromPath( modelRoot, modelPath );517 const viewPos = mapper.toViewPosition( modelPos );518 expect( viewPos.root ).to.equal( viewRoot );519 expect( getViewPath( viewPos ) ).to.deep.equal( viewPath );520 } );521 }522 testList( 'before first listItem', [ 1 ], [ 1 ] ); // --> before ul523 testList( 'beginning of first listItem', [ 1, 0 ], [ 1, 0, 0, 0 ] ); // --> beginning of `b` text node524 testList( 'end of first listItem', [ 1, 1 ], [ 1, 0, 0, 1 ] ); // --> end of `b` text node525 testList( 'before middle listItem', [ 2 ], [ 1, 1 ] ); // --> before middle li526 testList( 'before last listItem', [ 3 ], [ 1, 2 ] ); // --> before last li527 testList( 'after last listItem', [ 4 ], [ 2 ] ); // --> after ul528 testList( 'before numbered listItem', [ 5 ], [ 3 ] ); // --> before ol529 testList( 'after numbered listItem', [ 6 ], [ 4 ] ); // --> after ol530 } );531 } );532 describe( 'convert changes', () => {533 describe( 'insert', () => {534 testInsert(535 'list item at the beginning of same list type',536 '<paragraph>p</paragraph>' +537 '[<listItem listIndent="0" listType="bulleted">x</listItem>]' +538 '<listItem listIndent="0" listType="bulleted">a</listItem>',539 '<p>p</p>' +540 '<ul>' +541 '<li>x</li>' +542 '<li>a</li>' +543 '</ul>'544 );545 testInsert(546 'list item in the middle of same list type',547 '<paragraph>p</paragraph>' +548 '<listItem listIndent="0" listType="bulleted">a</listItem>' +549 '[<listItem listIndent="0" listType="bulleted">x</listItem>]' +550 '<listItem listIndent="0" listType="bulleted">b</listItem>',551 '<p>p</p>' +552 '<ul>' +553 '<li>a</li>' +554 '<li>x</li>' +555 '<li>b</li>' +556 '</ul>'557 );558 testInsert(559 'list item at the end of same list type',560 '<paragraph>p</paragraph>' +561 '<listItem listIndent="0" listType="bulleted">a</listItem>' +562 '[<listItem listIndent="0" listType="bulleted">x</listItem>]',563 '<p>p</p>' +564 '<ul>' +565 '<li>a</li>' +566 '<li>x</li>' +567 '</ul>'568 );569 testInsert(570 'list item at the beginning of different list type',571 '<paragraph>p</paragraph>' +572 '[<listItem listIndent="0" listType="numbered">x</listItem>]' +573 '<listItem listIndent="0" listType="bulleted">a</listItem>',574 '<p>p</p>' +575 '<ol>' +576 '<li>x</li>' +577 '</ol>' +578 '<ul>' +579 '<li>a</li>' +580 '</ul>'581 );582 testInsert(583 'list item in the middle of different list type',584 '<paragraph>p</paragraph>' +585 '<listItem listIndent="0" listType="bulleted">a</listItem>' +586 '[<listItem listIndent="0" listType="numbered">x</listItem>]' +587 '<listItem listIndent="0" listType="bulleted">b</listItem>',588 '<p>p</p>' +589 '<ul>' +590 '<li>a</li>' +591 '</ul>' +592 '<ol>' +593 '<li>x</li>' +594 '</ol>' +595 '<ul>' +596 '<li>b</li>' +597 '</ul>'598 );599 testInsert(600 'list item at the end of different list type',601 '<paragraph>p</paragraph>' +602 '<listItem listIndent="0" listType="bulleted">a</listItem>' +603 '[<listItem listIndent="0" listType="numbered">x</listItem>]',604 '<p>p</p>' +605 '<ul>' +606 '<li>a</li>' +607 '</ul>' +608 '<ol>' +609 '<li>x</li>' +610 '</ol>'611 );612 testInsert(613 'element between list items',614 '<listItem listIndent="0" listType="bulleted">a</listItem>' +615 '[<paragraph>x</paragraph>]' +616 '<listItem listIndent="0" listType="bulleted">a</listItem>',617 '<ul>' +618 '<li>a</li>' +619 '</ul>' +620 '<p>x</p>' +621 '<ul>' +622 '<li>a</li>' +623 '</ul>'624 );625 } );626 describe( 'remove', () => {627 testRemove(628 'remove the first list item',629 '<paragraph>p</paragraph>' +630 '[<listItem listIndent="0" listType="bulleted">a</listItem>]' +631 '<listItem listIndent="0" listType="bulleted">b</listItem>' +632 '<listItem listIndent="0" listType="bulleted">c</listItem>',633 '<p>p</p>' +634 '<ul>' +635 '<li>b</li>' +636 '<li>c</li>' +637 '</ul>'638 );639 testRemove(640 'remove list item from the middle',641 '<paragraph>p</paragraph>' +642 '<listItem listIndent="0" listType="bulleted">a</listItem>' +643 '[<listItem listIndent="0" listType="bulleted">b</listItem>]' +644 '<listItem listIndent="0" listType="bulleted">c</listItem>',645 '<p>p</p>' +646 '<ul>' +647 '<li>a</li>' +648 '<li>c</li>' +649 '</ul>'650 );651 testRemove(652 'remove the last list item',653 '<paragraph>p</paragraph>' +654 '<listItem listIndent="0" listType="bulleted">a</listItem>' +655 '<listItem listIndent="0" listType="bulleted">b</listItem>' +656 '[<listItem listIndent="0" listType="bulleted">c</listItem>]',657 '<p>p</p>' +658 '<ul>' +659 '<li>a</li>' +660 '<li>b</li>' +661 '</ul>'662 );663 testRemove(664 'remove the only list item',665 '<paragraph>p</paragraph>' +666 '[<listItem listIndent="0" listType="bulleted">x</listItem>]' +667 '<paragraph>p</paragraph>',668 '<p>p</p>' +669 '<p>p</p>'670 );671 testRemove(672 'remove element from between lists of same type',673 '<paragraph>p</paragraph>' +674 '<listItem listIndent="0" listType="bulleted">a</listItem>' +675 '[<paragraph>x</paragraph>]' +676 '<listItem listIndent="0" listType="bulleted">b</listItem>' +677 '<paragraph>p</paragraph>',678 '<p>p</p>' +679 '<ul>' +680 '<li>a</li>' +681 '<li>b</li>' +682 '</ul>' +683 '<p>p</p>'684 );685 testRemove(686 'remove element from between lists of different type',687 '<paragraph>p</paragraph>' +688 '<listItem listIndent="0" listType="bulleted">a</listItem>' +689 '[<paragraph>x</paragraph>]' +690 '<listItem listIndent="0" listType="numbered">b</listItem>' +691 '<paragraph>p</paragraph>',692 '<p>p</p>' +693 '<ul>' +694 '<li>a</li>' +695 '</ul>' +696 '<ol>' +697 '<li>b</li>' +698 '</ol>' +699 '<p>p</p>'700 );701 } );702 describe( 'change type', () => {703 testChangeType(704 'change first list item',705 '<paragraph>p</paragraph>' +706 '[<listItem listIndent="0" listType="bulleted">a</listItem>]' +707 '<listItem listIndent="0" listType="bulleted">b</listItem>' +708 '<listItem listIndent="0" listType="bulleted">c</listItem>',709 '<p>p</p>' +710 '<ol>' +711 '<li>a</li>' +712 '</ol>' +713 '<ul>' +714 '<li>b</li>' +715 '<li>c</li>' +716 '</ul>'717 );718 testChangeType(719 'change middle list item',720 '<paragraph>p</paragraph>' +721 '<listItem listIndent="0" listType="bulleted">a</listItem>' +722 '[<listItem listIndent="0" listType="bulleted">b</listItem>]' +723 '<listItem listIndent="0" listType="bulleted">c</listItem>',724 '<p>p</p>' +725 '<ul>' +726 '<li>a</li>' +727 '</ul>' +728 '<ol>' +729 '<li>b</li>' +730 '</ol>' +731 '<ul>' +732 '<li>c</li>' +733 '</ul>'734 );735 testChangeType(736 'change last list item',737 '<paragraph>p</paragraph>' +738 '<listItem listIndent="0" listType="bulleted">a</listItem>' +739 '<listItem listIndent="0" listType="bulleted">b</listItem>' +740 '[<listItem listIndent="0" listType="bulleted">c</listItem>]',741 '<p>p</p>' +742 '<ul>' +743 '<li>a</li>' +744 '<li>b</li>' +745 '</ul>' +746 '<ol>' +747 '<li>c</li>' +748 '</ol>'749 );750 testChangeType(751 'change only list item',752 '<paragraph>p</paragraph>' +753 '[<listItem listIndent="0" listType="bulleted">a</listItem>]' +754 '<paragraph>p</paragraph>',755 '<p>p</p>' +756 '<ol>' +757 '<li>a</li>' +758 '</ol>' +759 '<p>p</p>'760 );761 testChangeType(762 'change element at the edge of two different lists #1',763 '<listItem listIndent="0" listType="bulleted">a</listItem>' +764 '<listItem listIndent="0" listType="bulleted">b</listItem>' +765 '[<listItem listIndent="0" listType="bulleted">c</listItem>]' +766 '<listItem listIndent="0" listType="numbered">d</listItem>',767 '<ul>' +768 '<li>a</li>' +769 '<li>b</li>' +770 '</ul>' +771 '<ol>' +772 '<li>c</li>' +773 '<li>d</li>' +774 '</ol>'775 );776 testChangeType(777 'change element at the edge of two different lists #1',778 '<listItem listIndent="0" listType="numbered">a</listItem>' +779 '[<listItem listIndent="0" listType="bulleted">b</listItem>]' +780 '<listItem listIndent="0" listType="bulleted">c</listItem>' +781 '<listItem listIndent="0" listType="bulleted">d</listItem>',782 '<ol>' +783 '<li>a</li>' +784 '<li>b</li>' +785 '</ol>' +786 '<ul>' +787 '<li>c</li>' +788 '<li>d</li>' +789 '</ul>'790 );791 testChangeType(792 'change multiple elements #1',793 '<listItem listIndent="0" listType="bulleted">a</listItem>' +794 '[<listItem listIndent="0" listType="bulleted">b</listItem>' +795 '<listItem listIndent="0" listType="bulleted">c</listItem>]' +796 '<listItem listIndent="0" listType="bulleted">d</listItem>',797 '<ul>' +798 '<li>a</li>' +799 '</ul>' +800 '<ol>' +801 '<li>b</li>' +802 '<li>c</li>' +803 '</ol>' +804 '<ul>' +805 '<li>d</li>' +806 '</ul>'807 );808 testChangeType(809 'change multiple elements #2',810 '<listItem listIndent="0" listType="numbered">a</listItem>' +811 '[<listItem listIndent="0" listType="bulleted">b</listItem>' +812 '<listItem listIndent="0" listType="bulleted">c</listItem>]' +813 '<listItem listIndent="0" listType="numbered">d</listItem>',814 '<ol>' +815 '<li>a</li>' +816 '<li>b</li>' +817 '<li>c</li>' +818 '<li>d</li>' +819 '</ol>'820 );821 } );822 describe( 'rename from list item', () => {823 testRenameFromListItem(824 'rename first list item',825 '[<listItem listIndent="0" listType="bulleted">a</listItem>]' +826 '<listItem listIndent="0" listType="bulleted">b</listItem>',827 '<p>a</p>' +828 '<ul>' +829 '<li>b</li>' +830 '</ul>'831 );832 testRenameFromListItem(833 'rename middle list item',834 '<listItem listIndent="0" listType="bulleted">a</listItem>' +835 '[<listItem listIndent="0" listType="bulleted">b</listItem>]' +836 '<listItem listIndent="0" listType="bulleted">c</listItem>',837 '<ul>' +838 '<li>a</li>' +839 '</ul>' +840 '<p>b</p>' +841 '<ul>' +842 '<li>c</li>' +843 '</ul>'844 );845 testRenameFromListItem(846 'rename last list item',847 '<listItem listIndent="0" listType="bulleted">a</listItem>' +848 '[<listItem listIndent="0" listType="bulleted">b</listItem>]',849 '<ul>' +850 '<li>a</li>' +851 '</ul>' +852 '<p>b</p>'853 );854 testRenameFromListItem(855 'rename only list item',856 '<paragraph>p</paragraph>' +857 '[<listItem listIndent="0" listType="bulleted">x</listItem>]' +858 '<paragraph>p</paragraph>',859 '<p>p</p>' +860 '<p>x</p>' +861 '<p>p</p>'862 );863 } );864 describe( 'rename to list item (with attribute change)', () => {865 testRenameToListItem(866 'only paragraph', 0,867 '[<paragraph>a</paragraph>]',868 '<ul>' +869 '<li>a</li>' +870 '</ul>'871 );872 testRenameToListItem(873 'paragraph between paragraphs', 0,874 '<paragraph>x</paragraph>' +875 '[<paragraph>a</paragraph>]' +876 '<paragraph>x</paragraph>',877 '<p>x</p>' +878 '<ul>' +879 '<li>a</li>' +880 '</ul>' +881 '<p>x</p>'882 );883 testRenameToListItem(884 'element before list of same type', 0,885 '[<paragraph>x</paragraph>]' +886 '<listItem listIndent="0" listType="bulleted">a</listItem>',887 '<ul>' +888 '<li>x</li>' +889 '<li>a</li>' +890 '</ul>'891 );892 testRenameToListItem(893 'element after list of same type', 0,894 '<listItem listIndent="0" listType="bulleted">a</listItem>' +895 '[<paragraph>x</paragraph>]',896 '<ul>' +897 '<li>a</li>' +898 '<li>x</li>' +899 '</ul>'900 );901 testRenameToListItem(902 'element before list of different type', 0,903 '[<paragraph>x</paragraph>]' +904 '<listItem listIndent="0" listType="numbered">a</listItem>',905 '<ul>' +906 '<li>x</li>' +907 '</ul>' +908 '<ol>' +909 '<li>a</li>' +910 '</ol>'911 );912 testRenameToListItem(913 'element after list of different type', 0,914 '<listItem listIndent="0" listType="numbered">a</listItem>' +915 '[<paragraph>x</paragraph>]',916 '<ol>' +917 '<li>a</li>' +918 '</ol>' +919 '<ul>' +920 '<li>x</li>' +921 '</ul>'922 );923 testRenameToListItem(924 'element between lists of same type', 0,925 '<listItem listIndent="0" listType="bulleted">a</listItem>' +926 '[<paragraph>x</paragraph>]' +927 '<listItem listIndent="0" listType="bulleted">b</listItem>',928 '<ul>' +929 '<li>a</li>' +930 '<li>x</li>' +931 '<li>b</li>' +932 '</ul>'933 );934 } );935 describe( 'move', () => {936 testMove(937 'list item inside same list',938 '<paragraph>p</paragraph>' +939 '<listItem listIndent="0" listType="bulleted">a</listItem>' +940 '[<listItem listIndent="0" listType="bulleted">b</listItem>]' +941 '<listItem listIndent="0" listType="bulleted">c</listItem>',942 4, // Move after last item.943 '<p>p</p>' +944 '<ul>' +945 '<li>a</li>' +946 '<li>c</li>' +947 '<li>b</li>' +948 '</ul>'949 );950 testMove(951 'out list item from list',952 '<paragraph>p</paragraph>' +953 '<listItem listIndent="0" listType="bulleted">a</listItem>' +954 '[<listItem listIndent="0" listType="bulleted">b</listItem>]' +955 '<paragraph>p</paragraph>',956 4, // Move after second paragraph.957 '<p>p</p>' +958 '<ul>' +959 '<li>a</li>' +960 '</ul>' +961 '<p>p</p>' +962 '<ul>' +963 '<li>b</li>' +964 '</ul>'965 );966 testMove(967 'the only list item',968 '<paragraph>p</paragraph>' +969 '[<listItem listIndent="0" listType="bulleted">a</listItem>]' +970 '<paragraph>p</paragraph>',971 3, // Move after second paragraph.972 '<p>p</p>' +973 '<p>p</p>' +974 '<ul>' +975 '<li>a</li>' +976 '</ul>'977 );978 testMove(979 'list item between two lists of same type',980 '<listItem listIndent="0" listType="bulleted">a</listItem>' +981 '[<listItem listIndent="0" listType="bulleted">b</listItem>]' +982 '<paragraph>p</paragraph>' +983 '<listItem listIndent="0" listType="bulleted">c</listItem>' +984 '<listItem listIndent="0" listType="bulleted">d</listItem>',985 4, // Move between list item "c" and list item "d'.986 '<ul>' +987 '<li>a</li>' +988 '</ul>' +989 '<p>p</p>' +990 '<ul>' +991 '<li>c</li>' +992 '<li>b</li>' +993 '<li>d</li>' +994 '</ul>'995 );996 testMove(997 'list item between two lists of different type',998 '<listItem listIndent="0" listType="bulleted">a</listItem>' +999 '[<listItem listIndent="0" listType="bulleted">b</listItem>]' +1000 '<paragraph>p</paragraph>' +1001 '<listItem listIndent="0" listType="numbered">c</listItem>' +1002 '<listItem listIndent="0" listType="numbered">d</listItem>',1003 4, // Move between list item "c" and list item "d'.1004 '<ul>' +1005 '<li>a</li>' +1006 '</ul>' +1007 '<p>p</p>' +1008 '<ol>' +1009 '<li>c</li>' +1010 '</ol>' +1011 '<ul>' +1012 '<li>b</li>' +1013 '</ul>' +1014 '<ol>' +1015 '<li>d</li>' +1016 '</ol>'1017 );1018 testMove(1019 'element between list items',1020 '<listItem listIndent="0" listType="bulleted">a</listItem>' +1021 '<listItem listIndent="0" listType="bulleted">b</listItem>' +1022 '[<paragraph>p</paragraph>]',1023 1, // Move between list item "a" and list item "b'.1024 '<ul>' +1025 '<li>a</li>' +1026 '</ul>' +1027 '<p>p</p>' +1028 '<ul>' +1029 '<li>b</li>' +1030 '</ul>'1031 );1032 } );1033 } );1034 } );1035 describe( 'nested lists', () => {1036 describe( 'setting data', () => {1037 function testList( string, expectedString = null ) {1038 return () => {1039 editor.setData( string );1040 assertEqualMarkup( editor.getData(), expectedString || string );1041 };1042 }1043 describe( 'non HTML compliant list fixing', () => {1044 it( 'ul in ul', testList(1045 '<ul>' +1046 '<ul>' +1047 '<li>1.1</li>' +1048 '</ul>' +1049 '</ul>',1050 '<ul>' +1051 '<li>1.1</li>' +1052 '</ul>'1053 ) );1054 it( 'ul in ol', testList(1055 '<ol>' +1056 '<ul>' +1057 '<li>1.1</li>' +1058 '</ul>' +1059 '</ol>',1060 '<ul>' +1061 '<li>1.1</li>' +1062 '</ul>'1063 ) );1064 it( 'ul in ul (previous sibling is li)', testList(1065 '<ul>' +1066 '<li>1</li>' +1067 '<ul>' +1068 '<li>2.1</li>' +1069 '</ul>' +1070 '</ul>',1071 '<ul>' +1072 '<li>1' +1073 '<ul>' +1074 '<li>2.1</li>' +1075 '</ul>' +1076 '</li>' +1077 '</ul>'1078 ) );1079 it( 'ul in deeply nested ul - base index > 0 #1', testList(1080 '<ul>' +1081 '<li>1.1</li>' +1082 '<li>1.2' +1083 '<ul>' +1084 '<ul>' +1085 '<ul>' +1086 '<ul>' +1087 '<li>2.1</li>' +1088 '</ul>' +1089 '</ul>' +1090 '</ul>' +1091 '</ul>' +1092 '</li>' +1093 '</ul>',1094 '<ul>' +1095 '<li>1.1</li>' +1096 '<li>1.2' +1097 '<ul>' +1098 '<li>2.1</li>' +1099 '</ul>' +1100 '</li>' +1101 '</ul>'1102 ) );1103 it( 'ul in deeply nested ul - base index > 0 #2', testList(1104 '<ul>' +1105 '<li>1.1</li>' +1106 '<li>1.2' +1107 '<ul>' +1108 '<li>2.1</li>' +1109 '<ul>' +1110 '<ul>' +1111 '<ul>' +1112 '<li>3.1</li>' +1113 '</ul>' +1114 '</ul>' +1115 '</ul>' +1116 '<li>2.2</li>' +1117 '</ul>' +1118 '</li>' +1119 '</ul>',1120 '<ul>' +1121 '<li>1.1</li>' +1122 '<li>1.2' +1123 '<ul>' +1124 '<li>2.1' +1125 '<ul>' +1126 '<li>3.1</li>' +1127 '</ul>' +1128 '</li>' +1129 '<li>2.2</li>' +1130 '</ul>' +1131 '</li>' +1132 '</ul>'1133 ) );1134 it( 'ul in deeply nested ul inside li', testList(1135 '<ul>' +1136 '<li>A' +1137 '<ul>' +1138 '<ul>' +1139 '<ul>' +1140 '<ul>' +1141 '<li>B</li>' +1142 '</ul>' +1143 '</ul>' +1144 '</ul>' +1145 '<li>C</li>' +1146 '</ul>' +1147 '</li>' +1148 '</ul>',1149 '<ul>' +1150 '<li>A' +1151 '<ul>' +1152 '<li>B</li>' +1153 '<li>C</li>' +1154 '</ul>' +1155 '</li>' +1156 '</ul>'1157 ) );1158 it( 'ul in deeply nested ul/ol', testList(1159 '<ul>' +1160 '<li>A' +1161 '<ol>' +1162 '<ul>' +1163 '<ol>' +1164 '<ul>' +1165 '<li>B</li>' +1166 '</ul>' +1167 '</ol>' +1168 '</ul>' +1169 '<li>C</li>' +1170 '</ol>' +1171 '</li>' +1172 '</ul>',1173 '<ul>' +1174 '<li>A' +1175 '<ul>' +1176 '<li>B</li>' +1177 '<li>C</li>' +1178 '</ul>' +1179 '</li>' +1180 '</ul>'1181 ) );1182 it( 'ul in ul (complex case)', testList(1183 '<ol>' +1184 '<li>1</li>' +1185 '<ul>' +1186 '<li>A</li>' +1187 '<ol>' +1188 '<li>1</li>' +1189 '</ol>' +1190 '</ul>' +1191 '<li>2</li>' +1192 '<li>3</li>' +1193 '<ul>' +1194 '<li>A</li>' +1195 '<li>B</li>' +1196 '</ul>' +1197 '</ol>' +1198 '<ul>' +1199 '<li>A</li>' +1200 '<ol>' +1201 '<li>1</li>' +1202 '<li>2</li>' +1203 '</ol>' +1204 '</ul>',1205 '<ol>' +1206 '<li>1' +1207 '<ul>' +1208 '<li>A' +1209 '<ol>' +1210 '<li>1</li>' +1211 '</ol>' +1212 '</li>' +1213 '</ul>' +1214 '</li>' +1215 '<li>2</li>' +1216 '<li>3' +1217 '<ul>' +1218 '<li>A</li>' +1219 '<li>B</li>' +1220 '</ul>' +1221 '</li>' +1222 '</ol>' +1223 '<ul>' +1224 '<li>A' +1225 '<ol>' +1226 '<li>1</li>' +1227 '<li>2</li>' +1228 '</ol>' +1229 '</li>' +1230 '</ul>'1231 ) );1232 it( 'ol in ol (deep structure)', testList(1233 '<ol>' +1234 '<li>A1</li>' +1235 '<ol>' +1236 '<ol>' +1237 '<ol>' +1238 '<ol>' +1239 '<ol>' +1240 '<ol>' +1241 '<ol>' +1242 '<li>B8</li>' +1243 '</ol>' +1244 '</ol>' +1245 '</ol>' +1246 '</ol>' +1247 '</ol>' +1248 '<li>C3</li>' +1249 '<ol>' +1250 '<li>D4</li>' +1251 '</ol>' +1252 '</ol>' +1253 '<li>E2</li>' +1254 '</ol>' +1255 '</ol>',1256 '<ol>' +1257 '<li>A1' +1258 '<ol>' +1259 '<li>B8</li>' +1260 '<li>C3' +1261 '<ol>' +1262 '<li>D4</li>' +1263 '</ol>' +1264 '</li>' +1265 '<li>E2</li>' +1266 '</ol>' +1267 '</li>' +1268 '</ol>'1269 ) );1270 it( 'block elements wrapping nested ul', testList(1271 'text before' +1272 '<ul>' +1273 '<li>' +1274 'text' +1275 '<div>' +1276 '<ul>' +1277 '<li>inner text</li>' +1278 '</ul>' +1279 '</div>' +1280 '</li>' +1281 '</ul>',1282 '<p>text before</p>' +1283 '<ul>' +1284 '<li>' +1285 'text' +1286 '<ul>' +1287 '<li>inner text</li>' +1288 '</ul>' +1289 '</li>' +1290 '</ul>'1291 ) );1292 it( 'block elements wrapping nested ul - invalid blocks', testList(1293 '<ul>' +1294 '<li>' +1295 'a' +1296 '<table>' +1297 '<tr>' +1298 '<td>' +1299 '<div>' +1300 '<ul>' +1301 '<li>b</li>' +1302 '<li>c' +1303 '<ul>' +1304 '<li>' +1305 'd' +1306 '<table>' +1307 '<tr>' +1308 '<td>' +1309 'e' +1310 '</td>' +1311 '</tr>' +1312 '</table>' +1313 '</li>' +1314 '</ul>' +1315 '</li>' +1316 '</ul>' +1317 '</div>' +1318 '</td>' +1319 '</tr>' +1320 '</table>' +1321 'f' +1322 '</li>' +1323 '<li>g</li>' +1324 '</ul>',1325 '<ul>' +1326 '<li>a</li>' +1327 '</ul>' +1328 '<figure class="table">' +1329 '<table>' +1330 '<tbody>' +1331 '<tr>' +1332 '<td>' +1333 '<ul>' +1334 '<li>b</li>' +1335 '<li>c<ul>' +1336 '<li>d</li>' +1337 '</ul>' +1338 '</li>' +1339 '</ul>' +1340 '<p>e</p>' +1341 '</td>' +1342 '</tr>' +1343 '</tbody>' +1344 '</table>' +1345 '</figure>' +1346 '<ul>' +1347 '<li>f</li>' +1348 '<li>g</li>' +1349 '</ul>'1350 ) );1351 it( 'deeply nested block elements wrapping nested ul', testList(1352 '<ul>' +1353 '<li>' +1354 'a' +1355 '<div>' +1356 '<div>' +1357 '<ul>' +1358 '<li>b</li>' +1359 '<li>c' +1360 '<ul>' +1361 '<li>d' +1362 '<div>' +1363 '<ul>' +1364 '<li>e</li>' +1365 '</ul>' +1366 '</div>' +1367 '</li>' +1368 '</ul>' +1369 '</li>' +1370 '</ul>' +1371 '</div>' +1372 '</div>' +1373 'f' +1374 '</li>' +1375 '<li>g</li>' +1376 '</ul>' +1377 '</ul>',1378 '<ul>' +1379 '<li>a' +1380 '<ul>' +1381 '<li>b</li>' +1382 '<li>c' +1383 '<ul>' +1384 '<li>d' +1385 '<ul>' +1386 '<li>e</li>' +1387 '</ul>' +1388 '</li>' +1389 '</ul>' +1390 '</li>' +1391 '</ul>' +1392 '</li>' +1393 '<li>f</li>' +1394 '<li>g</li>' +1395 '</ul>'1396 ) );1397 } );1398 it( 'bullet list simple structure', testList(1399 '<p>foo</p>' +1400 '<ul>' +1401 '<li>' +1402 '1' +1403 '<ul>' +1404 '<li>1.1</li>' +1405 '</ul>' +1406 '</li>' +1407 '</ul>' +1408 '<p>bar</p>'1409 ) );1410 it( 'bullet list deep structure', testList(1411 '<p>foo</p>' +1412 '<ul>' +1413 '<li>' +1414 '1' +1415 '<ul>' +1416 '<li>' +1417 '1.1' +1418 '<ul><li>1.1.1</li><li>1.1.2</li><li>1.1.3</li><li>1.1.4</li></ul>' +1419 '</li>' +1420 '<li>' +1421 '1.2' +1422 '<ul><li>1.2.1</li></ul>' +1423 '</li>' +1424 '</ul>' +1425 '</li>' +1426 '<li>2</li>' +1427 '<li>' +1428 '3' +1429 '<ul>' +1430 '<li>' +1431 '3.1' +1432 '<ul>' +1433 '<li>' +1434 '3.1.1' +1435 '<ul><li>3.1.1.1</li></ul>' +1436 '</li>' +1437 '<li>3.1.2</li>' +1438 '</ul>' +1439 '</li>' +1440 '</ul>' +1441 '</li>' +1442 '</ul>' +1443 '<p>bar</p>'1444 ) );1445 it( 'mixed lists deep structure', testList(1446 '<p>foo</p>' +1447 '<ul>' +1448 '<li>' +1449 '1' +1450 '<ul>' +1451 '<li>' +1452 '1.1' +1453 '<ul><li>1.1.1</li><li>1.1.2</li></ul>' +1454 '<ol><li>1.1.3</li><li>1.1.4</li></ol>' +1455 '</li>' +1456 '<li>' +1457 '1.2' +1458 '<ul><li>1.2.1</li></ul>' +1459 '</li>' +1460 '</ul>' +1461 '</li>' +1462 '<li>2</li>' +1463 '<li>' +1464 '3' +1465 '<ol>' +1466 '<li>' +1467 '3.1' +1468 '<ul>' +1469 '<li>' +1470 '3.1.1' +1471 '<ol><li>3.1.1.1</li></ol>' +1472 '<ul><li>3.1.1.2</li></ul>' +1473 '</li>' +1474 '<li>3.1.2</li>' +1475 '</ul>' +1476 '</li>' +1477 '</ol>' +1478 '<ul>' +1479 '<li>3.2</li>' +1480 '<li>3.3</li>' +1481 '</ul>' +1482 '</li>' +1483 '</ul>' +1484 '<p>bar</p>',1485 '<p>foo</p>' +1486 '<ul>' +1487 '<li>' +1488 '1' +1489 '<ul>' +1490 '<li>' +1491 '1.1' +1492 '<ul><li>1.1.1</li><li>1.1.2</li><li>1.1.3</li><li>1.1.4</li></ul>' +1493 '</li>' +1494 '<li>' +1495 '1.2' +1496 '<ul><li>1.2.1</li></ul>' +1497 '</li>' +1498 '</ul>' +1499 '</li>' +1500 '<li>2</li>' +1501 '<li>' +1502 '3' +1503 '<ol>' +1504 '<li>' +1505 '3.1' +1506 '<ul>' +1507 '<li>' +1508 '3.1.1' +1509 '<ol><li>3.1.1.1</li><li>3.1.1.2</li></ol>' +1510 '</li>' +1511 '<li>3.1.2</li>' +1512 '</ul>' +1513 '</li>' +1514 '<li>3.2</li>' +1515 '<li>3.3</li>' +1516 '</ol>' +1517 '</li>' +1518 '</ul>' +1519 '<p>bar</p>'1520 ) );1521 it( 'mixed lists deep structure, white spaces, incorrect content, empty items', testList(1522 '<p>foo</p>' +1523 '<ul>' +1524 ' xxx' +1525 ' <li>' +1526 ' 1' +1527 ' <ul>' +1528 ' xxx' +1529 ' <li>' +1530 ' <ul><li></li><li>1.1.2</li></ul>' +1531 ' <ol><li>1.1.3</li><li>1.1.4</li></ol>' + // Will be changed to <ul>.1532 ' </li>' +1533 ' <li>' +1534 ' <ul><li>1.2.1</li></ul>' +1535 ' </li>' +1536 ' xxx' +1537 ' </ul>' +1538 ' </li>' +1539 ' <li>2</li>' +1540 ' <li>' +1541 ' <ol>' +1542 ' <p>xxx</p>' +1543 ' <li>' +1544 ' 3<strong>.</strong>1' + // Test multiple text nodes in <li>.1545 ' <ul>' +1546 ' <li>' +1547 ' 3.1.1' +1548 ' <ol><li>3.1.1.1</li></ol>' +1549 ' <ul><li>3.1.1.2</li></ul>' + // Will be changed to <ol>.1550 ' </li>' +1551 ' <li>3.1.2</li>' +1552 ' </ul>' +1553 ' </li>' +1554 ' </ol>' +1555 ' <p>xxx</p>' +1556 ' <ul>' + // Since <p> gets removed, this will become <ol>.1557 ' <li>3.2</li>' +1558 ' <li>3.3</li>' +1559 ' </ul>' +1560 ' </li>' +1561 ' <p>xxx</p>' +1562 '</ul>' +1563 '<p>bar</p>',1564 '<p>foo</p>' +1565 '<ul>' +1566 '<li>' +1567 '1' +1568 '<ul>' +1569 '<li>' +1570 ' ' +1571 '<ul>' +1572 '<li> </li>' +1573 '<li>1.1.2</li>' +1574 '<li>1.1.3</li>' +1575 '<li>1.1.4</li>' +1576 '</ul>' +1577 '</li>' +1578 '<li>' +1579 ' ' +1580 '<ul><li>1.2.1</li></ul>' +1581 '</li>' +1582 '</ul>' +1583 '</li>' +1584 '<li>2</li>' +1585 '<li>' +1586 ' ' +1587 '<ol>' +1588 '<li>' +1589 '3<strong>.</strong>1' +1590 '<ul>' +1591 '<li>' +1592 '3.1.1' +1593 '<ol>' +1594 '<li>3.1.1.1</li>' +1595 '<li>3.1.1.2</li>' +1596 '</ol>' +1597 '</li>' +1598 '<li>3.1.2</li>' +1599 '</ul>' +1600 '</li>' +1601 '<li>3.2</li>' +1602 '<li>3.3</li>' +1603 '</ol>' +1604 '</li>' +1605 '</ul>' +1606 '<p>bar</p>'1607 ) );1608 describe( 'model tests for nested lists', () => {1609 it( 'should properly set listIndent and listType', () => {1610 // <ol> in the middle will be fixed by postfixer to bulleted list.1611 editor.setData(1612 '<p>foo</p>' +1613 '<ul>' +1614 '<li>' +1615 '1' +1616 '<ul>' +1617 '<li>1.1</li>' +1618 '</ul>' +1619 '<ol>' +1620 '<li>' +1621 '1.2' +1622 '<ol>' +1623 '<li>1.2.1</li>' +1624 '</ol>' +1625 '</li>' +1626 '<li>1.3</li>' +1627 '</ol>' +1628 '</li>' +1629 '<li>2</li>' +1630 '</ul>' +1631 '<p>bar</p>'1632 );1633 const expectedModelData =1634 '<paragraph>foo</paragraph>' +1635 '<listItem listIndent="0" listType="bulleted">1</listItem>' +1636 '<listItem listIndent="1" listType="bulleted">1.1</listItem>' +1637 '<listItem listIndent="1" listType="bulleted">1.2</listItem>' +1638 '<listItem listIndent="2" listType="numbered">1.2.1</listItem>' +1639 '<listItem listIndent="1" listType="bulleted">1.3</listItem>' +1640 '<listItem listIndent="0" listType="bulleted">2</listItem>' +1641 '<paragraph>bar</paragraph>';1642 assertEqualMarkup( getModelData( model, { withoutSelection: true } ), expectedModelData );1643 } );1644 it( 'should properly listIndent when list nested in other block', () => {1645 editor.setData(1646 '<ul>' +1647 '<li>' +1648 'a' +1649 '<table>' +1650 '<tr>' +1651 '<td>' +1652 '<div>' +1653 '<ul>' +1654 '<li>b</li>' +1655 '<li>c' +1656 '<ul>' +1657 '<li>' +1658 'd' +1659 '<table>' +1660 '<tr>' +1661 '<td>e</td>' +1662 '</tr>' +1663 '</table>' +1664 '</li>' +1665 '</ul>' +1666 '</li>' +1667 '</ul>' +1668 '</div>' +1669 '</td>' +1670 '</tr>' +1671 '</table>' +1672 'f' +1673 '</li>' +1674 '<li>g</li>' +1675 '</ul>'1676 );1677 const expectedModelData =1678 '<listItem listIndent="0" listType="bulleted">a</listItem>' +1679 '<table>' +1680 '<tableRow>' +1681 '<tableCell>' +1682 '<listItem listIndent="0" listType="bulleted">b</listItem>' +1683 '<listItem listIndent="0" listType="bulleted">c</listItem>' +1684 '<listItem listIndent="1" listType="bulleted">d</listItem>' +1685 '<paragraph>e</paragraph>' +1686 '</tableCell>' +1687 '</tableRow>' +1688 '</table>' +1689 '<listItem listIndent="0" listType="bulleted">f</listItem>' +1690 '<listItem listIndent="0" listType="bulleted">g</listItem>';1691 assertEqualMarkup( getModelData( model, { withoutSelection: true } ), expectedModelData );1692 } );1693 } );1694 } );1695 describe( 'position mapping', () => {1696 let mapper;1697 beforeEach( () => {1698 mapper = editor.editing.mapper;1699 editor.setData(1700 '<ul>' +1701 '<li>a</li>' +1702 '<li>' +1703 'bbb' +1704 '<ol>' +1705 '<li>c</li>' +1706 '<li>d</li>' +1707 '<li>e</li>' +1708 '<li>' +1709 '<ul>' +1710 '<li>g</li>' +1711 '<li>h</li>' +1712 '<li>i</li>' +1713 '</ul>' +1714 '</li>' +1715 '<li>j</li>' +1716 '</ol>' +1717 '</li>' +1718 '<li>k</li>' +1719 '</ul>'1720 );1721 } );1722 /*1723 <listItem listIndent=0 listType="bulleted">a</listItem>1724 <listItem listIndent=0 listType="bulleted">bbb</listItem>1725 <listItem listIndent=1 listType="numbered">c</listItem>1726 <listItem listIndent=1 listType="numbered">d</listItem>1727 <listItem listIndent=1 listType="numbered">e</listItem>1728 <listItem listIndent=1 listType="numbered"></listItem>1729 <listItem listIndent=2 listType="bulleted">g</listItem>1730 <listItem listIndent=2 listType="bulleted">h</listItem>1731 <listItem listIndent=2 listType="bullered">i</listItem>1732 <listItem listIndent=1 listType="numbered">j</listItem>1733 <listItem listIndent=0 listType="bulleted">k</listItem>1734 */1735 describe( 'view to model', () => {1736 function testList( testName, viewPath, modelPath ) {1737 it( testName, () => {1738 const viewPos = getViewPosition( viewRoot, viewPath, view );1739 const modelPos = mapper.toModelPosition( viewPos );1740 expect( modelPos.root ).to.equal( modelRoot );1741 expect( modelPos.path ).to.deep.equal( modelPath );1742 } );1743 }1744 testList( 'before ul#1', [ 0 ], [ 0 ] ); // --> before listItem "a"1745 testList( 'before li "a"', [ 0, 0 ], [ 0 ] ); // --> before listItem "a"1746 testList( 'before "a"', [ 0, 0, 0 ], [ 0, 0 ] ); // --> beginning of listItem "a"1747 testList( 'after "a"', [ 0, 0, 1 ], [ 0, 1 ] ); // --> end of listItem "a"1748 testList( 'before li "bbb"', [ 0, 1 ], [ 1 ] ); // --> before listItem "bbb"1749 testList( 'before "bbb"', [ 0, 1, 0 ], [ 1, 0 ] ); // --> beginning of listItem "bbb"1750 testList( 'after "bbb"', [ 0, 1, 1 ], [ 1, 3 ] ); // --> end of listItem "bbb"1751 testList( 'before li "c"', [ 0, 1, 1, 0 ], [ 2 ] ); // --> before listItem "c"1752 testList( 'before "c"', [ 0, 1, 1, 0, 0 ], [ 2, 0 ] ); // --> beginning of listItem "c"1753 testList( 'after "c"', [ 0, 1, 1, 0, 1 ], [ 2, 1 ] ); // --> end of listItem "c"1754 testList( 'before li "d"', [ 0, 1, 1, 1 ], [ 3 ] ); // --> before listItem "d"1755 testList( 'before li "e"', [ 0, 1, 1, 2 ], [ 4 ] ); // --> before listItem "e"1756 testList( 'before "empty" li', [ 0, 1, 1, 3 ], [ 5 ] ); // --> before "empty" listItem1757 testList( 'before ul#2', [ 0, 1, 1, 3, 0 ], [ 5, 0 ] ); // --> inside "empty" listItem1758 testList( 'before li "g"', [ 0, 1, 1, 3, 0, 0 ], [ 6 ] ); // --> before listItem "g"1759 testList( 'before li "h"', [ 0, 1, 1, 3, 0, 1 ], [ 7 ] ); // --> before listItem "h"1760 testList( 'before li "i"', [ 0, 1, 1, 3, 0, 2 ], [ 8 ] ); // --> before listItem "i"1761 testList( 'after li "i"', [ 0, 1, 1, 3, 0, 3 ], [ 9 ] ); // --> before listItem "j"1762 testList( 'after ul#2', [ 0, 1, 1, 3, 1 ], [ 9 ] ); // --> before listItem "j"1763 testList( 'before li "j"', [ 0, 1, 1, 4 ], [ 9 ] ); // --> before listItem "j"1764 testList( 'after li "j"', [ 0, 1, 1, 5 ], [ 10 ] ); // --> before listItem "k"1765 testList( 'end of li "bbb"', [ 0, 1, 2 ], [ 10 ] ); // --> before listItem "k"1766 testList( 'before li "k"', [ 0, 2 ], [ 10 ] ); // --> before listItem "k"1767 testList( 'after li "k"', [ 0, 3 ], [ 11 ] ); // --> after listItem "k"1768 testList( 'after ul', [ 1 ], [ 11 ] ); // --> after listItem "k"1769 } );1770 describe( 'model to view', () => {1771 function testList( testName, modelPath, viewPath ) {1772 it( testName, () => {1773 const modelPos = model.createPositionFromPath( modelRoot, modelPath );1774 const viewPos = mapper.toViewPosition( modelPos );1775 expect( viewPos.root ).to.equal( viewRoot );1776 expect( getViewPath( viewPos ) ).to.deep.equal( viewPath );1777 } );1778 }1779 testList( 'before listItem "a"', [ 0 ], [ 0 ] ); // --> before ul1780 testList( 'beginning of listItem "a"', [ 0, 0 ], [ 0, 0, 0, 0 ] ); // --> beginning of "a" text node1781 testList( 'end of listItem "a"', [ 0, 1 ], [ 0, 0, 0, 1 ] ); // --> end of "a" text node1782 testList( 'before listItem "bbb"', [ 1 ], [ 0, 1 ] ); // --> before li "bbb"1783 testList( 'beginning of listItem "bbb"', [ 1, 0 ], [ 0, 1, 0, 0 ] ); // --> beginning of "bbb" text node1784 testList( 'end of listItem "bbb"', [ 1, 3 ], [ 0, 1, 0, 3 ] ); // --> end of "bbb" text node1785 testList( 'before listItem "c"', [ 2 ], [ 0, 1, 1, 0 ] ); // --> before li "c"1786 testList( 'beginning of listItem "c"', [ 2, 0 ], [ 0, 1, 1, 0, 0, 0 ] ); // --> beginning of "c" text node1787 testList( 'end of listItem "c"', [ 2, 1 ], [ 0, 1, 1, 0, 0, 1 ] ); // --> end of "c" text node1788 testList( 'before listItem "d"', [ 3 ], [ 0, 1, 1, 1 ] ); // --> before li "d"1789 testList( 'before listItem "e"', [ 4 ], [ 0, 1, 1, 2 ] ); // --> before li "e"1790 testList( 'before "empty" listItem', [ 5 ], [ 0, 1, 1, 3 ] ); // --> before "empty" li1791 testList( 'inside "empty" listItem', [ 5, 0 ], [ 0, 1, 1, 3, 0 ] ); // --> before ul1792 testList( 'before listItem "g"', [ 6 ], [ 0, 1, 1, 3, 0, 0 ] ); // --> before li "g"1793 testList( 'before listItem "h"', [ 7 ], [ 0, 1, 1, 3, 0, 1 ] ); // --> before li "h"1794 testList( 'before listItem "i"', [ 8 ], [ 0, 1, 1, 3, 0, 2 ] ); // --> before li "i"1795 testList( 'before listItem "j"', [ 9 ], [ 0, 1, 1, 4 ] ); // --> before li "j"1796 testList( 'before listItem "k"', [ 10 ], [ 0, 2 ] ); // --> before li "k"1797 testList( 'after listItem "k"', [ 11 ], [ 1 ] ); // --> after ul1798 } );1799 } );1800 describe( 'convert changes', () => {1801 describe( 'insert', () => {1802 describe( 'same list type', () => {1803 testInsert(1804 'after smaller indent',1805 '<paragraph>p</paragraph>' +1806 '<listItem listIndent="0" listType="bulleted">1</listItem>' +1807 '[<listItem listIndent="1" listType="bulleted">x</listItem>]',1808 '<p>p</p>' +1809 '<ul>' +1810 '<li>' +1811 '1' +1812 '<ul>' +1813 '<li>x</li>' +1814 '</ul>' +1815 '</li>' +1816 '</ul>'1817 );1818 testInsert(1819 'after smaller indent, before same indent',1820 '<paragraph>p</paragraph>' +1821 '<listItem listIndent="0" listType="bulleted">1</listItem>' +1822 '[<listItem listIndent="1" listType="bulleted">x</listItem>]' +1823 '<listItem listIndent="1" listType="bulleted">1.1</listItem>',1824 '<p>p</p>' +1825 '<ul>' +1826 '<li>' +1827 '1' +1828 '<ul>' +1829 '<li>x</li>' +1830 '<li>1.1</li>' +1831 '</ul>' +1832 '</li>' +1833 '</ul>'1834 );1835 testInsert(1836 'after smaller indent, before smaller indent',1837 '<paragraph>p</paragraph>' +1838 '<listItem listIndent="0" listType="bulleted">1</listItem>' +1839 '[<listItem listIndent="1" listType="bulleted">x</listItem>]' +1840 '<listItem listIndent="0" listType="bulleted">2</listItem>',1841 '<p>p</p>' +1842 '<ul>' +1843 '<li>' +1844 '1' +1845 '<ul>' +1846 '<li>x</li>' +1847 '</ul>' +1848 '</li>' +1849 '<li>2</li>' +1850 '</ul>'1851 );1852 testInsert(1853 'after same indent',1854 '<paragraph>p</paragraph>' +1855 '<listItem listIndent="0" listType="bulleted">1</listItem>' +1856 '<listItem listIndent="1" listType="bulleted">1.1</listItem>' +1857 '[<listItem listIndent="1" listType="bulleted">x</listItem>]',1858 '<p>p</p>' +1859 '<ul>' +1860 '<li>' +1861 '1' +1862 '<ul>' +1863 '<li>1.1</li>' +1864 '<li>x</li>' +1865 '</ul>' +1866 '</li>' +1867 '</ul>'1868 );1869 testInsert(1870 'after same indent, before bigger indent',1871 '<paragraph>p</paragraph>' +1872 '<listItem listIndent="0" listType="bulleted">1</listItem>' +1873 '[<listItem listIndent="0" listType="bulleted">x</listItem>]' +1874 '<listItem listIndent="1" listType="bulleted">1.1</listItem>',1875 '<p>p</p>' +1876 '<ul>' +1877 '<li>1</li>' +1878 '<li>' +1879 'x' +1880 '<ul>' +1881 '<li>1.1</li>' +1882 '</ul>' +1883 '</li>' +1884 '</ul>'1885 );1886 testInsert(1887 'after bigger indent, before bigger indent',1888 '<paragraph>p</paragraph>' +1889 '<listItem listIndent="0" listType="bulleted">1</listItem>' +1890 '<listItem listIndent="1" listType="bulleted">1.1</listItem>' +1891 '[<listItem listIndent="0" listType="bulleted">x</listItem>]' +1892 '<listItem listIndent="1" listType="bulleted">1.2</listItem>',1893 '<p>p</p>' +1894 '<ul>' +1895 '<li>' +1896 '1' +1897 '<ul>' +1898 '<li>1.1</li>' +1899 '</ul>' +1900 '</li>' +1901 '<li>' +1902 'x' +1903 '<ul>' +1904 '<li>1.2</li>' +1905 '</ul>' +1906 '</li>' +1907 '</ul>'1908 );1909 testInsert(1910 'list items with too big indent',1911 '<listItem listIndent="0" listType="bulleted">a</listItem>' +1912 '<listItem listIndent="1" listType="bulleted">b</listItem>' +1913 '[<listItem listIndent="4" listType="bulleted">x</listItem>' + // This indent should be fixed by post fixer.1914 '<listItem listIndent="5" listType="bulleted">x</listItem>' + // This indent should be fixed by post fixer.1915 '<listItem listIndent="4" listType="bulleted">x</listItem>]' + // This indent should be fixed by post fixer.1916 '<listItem listIndent="1" listType="bulleted">c</listItem>',1917 '<ul>' +1918 '<li>' +1919 'a' +1920 '<ul>' +1921 '<li>' +1922 'b' +1923 '<ul>' +1924 '<li>' +1925 'x' +1926 '<ul>' +1927 '<li>x</li>' +1928 '</ul>' +1929 '</li>' +1930 '<li>x</li>' +1931 '</ul>' +1932 '</li>' +1933 '<li>c</li>' +1934 '</ul>' +1935 '</li>' +1936 '</ul>'1937 );1938 } );1939 describe( 'different list type', () => {1940 testInsert(1941 'after smaller indent, before same indent',1942 '<paragraph>p</paragraph>' +1943 '<listItem listIndent="0" listType="bulleted">1</listItem>' +1944 '[<listItem listIndent="1" listType="numbered">x</listItem>]' + // This type should be fixed by post fixer.1945 '<listItem listIndent="1" listType="bulleted">1.1</listItem>',1946 '<p>p</p>' +1947 '<ul>' +1948 '<li>' +1949 '1' +1950 '<ol>' +1951 '<li>x</li>' +1952 '<li>1.1</li>' +1953 '</ol>' +1954 '</li>' +1955 '</ul>'1956 );1957 testInsert(1958 'after same indent',1959 '<paragraph>p</paragraph>' +1960 '<listItem listIndent="0" listType="bulleted">1</listItem>' +1961 '<listItem listIndent="1" listType="bulleted">1.1</listItem>' +1962 '[<listItem listIndent="1" listType="numbered">x</listItem>]', // This type should be fixed by post fixer.1963 '<p>p</p>' +1964 '<ul>' +1965 '<li>' +1966 '1' +1967 '<ul>' +1968 '<li>1.1</li>' +1969 '<li>x</li>' +1970 '</ul>' +1971 '</li>' +1972 '</ul>'1973 );1974 testInsert(1975 'after same indent, before bigger indent',1976 '<paragraph>p</paragraph>' +1977 '<listItem listIndent="0" listType="bulleted">1</listItem>' +1978 '[<listItem listIndent="0" listType="numbered">x</listItem>]' +1979 '<listItem listIndent="1" listType="bulleted">1.1</listItem>',1980 '<p>p</p>' +1981 '<ul>' +1982 '<li>1</li>' +1983 '</ul>' +1984 '<ol>' +1985 '<li>' +1986 'x' +1987 '<ul>' +1988 '<li>1.1</li>' +1989 '</ul>' +1990 '</li>' +1991 '</ol>'1992 );1993 testInsert(1994 'after bigger indent, before bigger indent',1995 '<paragraph>p</paragraph>' +1996 '<listItem listIndent="0" listType="bulleted">1</listItem>' +1997 '<listItem listIndent="1" listType="bulleted">1.1</listItem>' +1998 '[<listItem listIndent="0" listType="numbered">x</listItem>]' +1999 '<listItem listIndent="1" listType="bulleted">1.2</listItem>',2000 '<p>p</p>' +2001 '<ul>' +2002 '<li>' +2003 '1' +2004 '<ul>' +2005 '<li>1.1</li>' +2006 '</ul>' +2007 '</li>' +2008 '</ul>' +2009 '<ol>' +2010 '<li>' +2011 'x' +2012 '<ul>' +2013 '<li>1.2</li>' +2014 '</ul>' +2015 '</li>' +2016 '</ol>'2017 );2018 testInsert(2019 'after bigger indent, in nested list, different type',2020 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2021 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2022 '<listItem listIndent="2" listType="bulleted">c</listItem>' +2023 '[<listItem listIndent="1" listType="numbered">x</listItem>]', // This type should be fixed by post fixer.2024 '<ul>' +2025 '<li>' +2026 'a' +2027 '<ul>' +2028 '<li>' +2029 'b' +2030 '<ul>' +2031 '<li>c</li>' +2032 '</ul>' +2033 '</li>' +2034 '<li>x</li>' +2035 '</ul>' +2036 '</li>' +2037 '</ul>'2038 );2039 } );2040 // This case is pretty complex but it tests various edge cases concerning splitting lists.2041 testInsert(2042 'element between nested list items - complex',2043 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2044 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2045 '<listItem listIndent="2" listType="bulleted">c</listItem>' +2046 '<listItem listIndent="3" listType="numbered">d</listItem>' +2047 '[<paragraph>x</paragraph>]' +2048 '<listItem listIndent="3" listType="numbered">e</listItem>' + // This indent should be fixed by post fixer.2049 '<listItem listIndent="2" listType="bulleted">f</listItem>' + // This indent should be fixed by post fixer.2050 '<listItem listIndent="3" listType="bulleted">g</listItem>' + // This indent should be fixed by post fixer.2051 '<listItem listIndent="1" listType="bulleted">h</listItem>' + // This indent should be fixed by post fixer.2052 '<listItem listIndent="2" listType="numbered">i</listItem>' + // This indent should be fixed by post fixer.2053 '<listItem listIndent="0" listType="numbered">j</listItem>' + // This indent should be fixed by post fixer.2054 '<paragraph>p</paragraph>',2055 '<ul>' +2056 '<li>' +2057 'a' +2058 '<ul>' +2059 '<li>' +2060 'b' +2061 '<ul>' +2062 '<li>' +2063 'c' +2064 '<ol>' +2065 '<li>d</li>' +2066 '</ol>' +2067 '</li>' +2068 '</ul>' +2069 '</li>' +2070 '</ul>' +2071 '</li>' +2072 '</ul>' +2073 '<p>x</p>' +2074 '<ol>' +2075 '<li>e</li>' +2076 '</ol>' +2077 '<ul>' +2078 '<li>' +2079 'f' +2080 '<ul>' +2081 '<li>g</li>' +2082 '</ul>' +2083 '</li>' +2084 '<li>' +2085 'h' +2086 '<ol>' +2087 '<li>i</li>' +2088 '</ol>' +2089 '</li>' +2090 '</ul>' +2091 '<ol>' +2092 '<li>j</li>' +2093 '</ol>' +2094 '<p>p</p>',2095 false2096 );2097 testInsert(2098 'element before indent "hole"',2099 '<listItem listIndent="0" listType="bulleted">1</listItem>' +2100 '<listItem listIndent="1" listType="bulleted">1.1</listItem>' +2101 '[<paragraph>x</paragraph>]' +2102 '<listItem listIndent="2" listType="bulleted">1.1.1</listItem>' + // This indent should be fixed by post fixer.2103 '<listItem listIndent="0" listType="bulleted">2</listItem>',2104 '<ul>' +2105 '<li>' +2106 '1' +2107 '<ul>' +2108 '<li>1.1</li>' +2109 '</ul>' +2110 '</li>' +2111 '</ul>' +2112 '<p>x</p>' +2113 '<ul>' +2114 '<li>1.1.1</li>' +2115 '<li>2</li>' +2116 '</ul>',2117 false2118 );2119 _test(2120 'two list items with mismatched types inserted in one batch',2121 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2122 '<listItem listIndent="1" listType="bulleted">b</listItem>[]',2123 '<ul>' +2124 '<li>' +2125 'a' +2126 '<ul>' +2127 '<li>b</li>' +2128 '<li>c</li>' +2129 '<li>d</li>' +2130 '</ul>' +2131 '</li>' +2132 '</ul>',2133 () => {2134 const item1 = '<listItem listIndent="1" listType="numbered">c</listItem>';2135 const item2 = '<listItem listIndent="1" listType="bulleted">d</listItem>';2136 model.change( writer => {2137 writer.append( parseModel( item1, model.schema ), modelRoot );2138 writer.append( parseModel( item2, model.schema ), modelRoot );2139 } );2140 }2141 );2142 } );2143 describe( 'remove', () => {2144 testRemove(2145 'the first nested item',2146 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2147 '[<listItem listIndent="1" listType="bulleted">b</listItem>]' +2148 '<listItem listIndent="1" listType="bulleted">c</listItem>',2149 '<ul>' +2150 '<li>' +2151 'a' +2152 '<ul>' +2153 '<li>c</li>' +2154 '</ul>' +2155 '</li>' +2156 '</ul>'2157 );2158 testRemove(2159 'nested item from the middle',2160 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2161 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2162 '[<listItem listIndent="1" listType="bulleted">c</listItem>]' +2163 '<listItem listIndent="1" listType="bulleted">d</listItem>',2164 '<ul>' +2165 '<li>' +2166 'a' +2167 '<ul>' +2168 '<li>b</li>' +2169 '<li>d</li>' +2170 '</ul>' +2171 '</li>' +2172 '</ul>'2173 );2174 testRemove(2175 'the last nested item',2176 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2177 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2178 '[<listItem listIndent="1" listType="bulleted">c</listItem>]',2179 '<ul>' +2180 '<li>' +2181 'a' +2182 '<ul>' +2183 '<li>b</li>' +2184 '</ul>' +2185 '</li>' +2186 '</ul>'2187 );2188 testRemove(2189 'the only nested item',2190 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2191 '[<listItem listIndent="1" listType="bulleted">c</listItem>]',2192 '<ul>' +2193 '<li>a</li>' +2194 '</ul>'2195 );2196 testRemove(2197 'list item that separates two nested lists of same type',2198 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2199 '<listItem listIndent="1" listType="numbered">b</listItem>' +2200 '[<listItem listIndent="0" listType="bulleted">c</listItem>]' +2201 '<listItem listIndent="1" listType="numbered">d</listItem>',2202 '<ul>' +2203 '<li>' +2204 'a' +2205 '<ol>' +2206 '<li>b</li>' +2207 '<li>d</li>' +2208 '</ol>' +2209 '</li>' +2210 '</ul>'2211 );2212 testRemove(2213 'list item that separates two nested lists of different type',2214 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2215 '<listItem listIndent="1" listType="numbered">b</listItem>' +2216 '[<listItem listIndent="0" listType="bulleted">c</listItem>]' +2217 '<listItem listIndent="1" listType="bulleted">d</listItem>', // This type should be fixed by post fixer.2218 '<ul>' +2219 '<li>' +2220 'a' +2221 '<ol>' +2222 '<li>b</li>' +2223 '<li>d</li>' +2224 '</ol>' +2225 '</li>' +2226 '</ul>'2227 );2228 testRemove(2229 'item that has nested lists, previous item has same indent',2230 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2231 '[<listItem listIndent="0" listType="bulleted">b</listItem>]' +2232 '<listItem listIndent="1" listType="bulleted">c</listItem>' +2233 '<listItem listIndent="1" listType="bulleted">d</listItem>',2234 '<ul>' +2235 '<li>' +2236 'a' +2237 '<ul>' +2238 '<li>c</li>' +2239 '<li>d</li>' +2240 '</ul>' +2241 '</li>' +2242 '</ul>'2243 );2244 testRemove(2245 'item that has nested lists, previous item has smaller indent',2246 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2247 '[<listItem listIndent="1" listType="bulleted">b</listItem>]' +2248 '<listItem listIndent="2" listType="bulleted">c</listItem>' + // This indent should be fixed by post fixer.2249 '<listItem listIndent="2" listType="bulleted">d</listItem>', // This indent should be fixed by post fixer.2250 '<ul>' +2251 '<li>' +2252 'a' +2253 '<ul>' +2254 '<li>c</li>' +2255 '<li>d</li>' +2256 '</ul>' +2257 '</li>' +2258 '</ul>'2259 );2260 testRemove(2261 'item that has nested lists, previous item has bigger indent by 1',2262 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2263 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2264 '[<listItem listIndent="0" listType="bulleted">c</listItem>]' +2265 '<listItem listIndent="1" listType="bulleted">d</listItem>' +2266 '<listItem listIndent="2" listType="numbered">e</listItem>',2267 '<ul>' +2268 '<li>' +2269 'a' +2270 '<ul>' +2271 '<li>b</li>' +2272 '<li>' +2273 'd' +2274 '<ol>' +2275 '<li>e</li>' +2276 '</ol>' +2277 '</li>' +2278 '</ul>' +2279 '</li>' +2280 '</ul>'2281 );2282 testRemove(2283 'item that has nested lists, previous item has bigger indent by 2',2284 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2285 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2286 '<listItem listIndent="2" listType="bulleted">c</listItem>' +2287 '[<listItem listIndent="0" listType="bulleted">d</listItem>]' +2288 '<listItem listIndent="1" listType="bulleted">e</listItem>',2289 '<ul>' +2290 '<li>' +2291 'a' +2292 '<ul>' +2293 '<li>' +2294 'b' +2295 '<ul>' +2296 '<li>c</li>' +2297 '</ul>' +2298 '</li>' +2299 '<li>e</li>' +2300 '</ul>' +2301 '</li>' +2302 '</ul>'2303 );2304 testRemove(2305 'first list item that has nested list',2306 '[<listItem listIndent="0" listType="bulleted">a</listItem>]' +2307 '<listItem listIndent="1" listType="bulleted">b</listItem>' + // This indent should be fixed by post fixer.2308 '<listItem listIndent="2" listType="bulleted">c</listItem>', // This indent should be fixed by post fixer.2309 '<ul>' +2310 '<li>' +2311 'b' +2312 '<ul>' +2313 '<li>c</li>' +2314 '</ul>' +2315 '</li>' +2316 '</ul>'2317 );2318 } );2319 describe( 'change type', () => {2320 testChangeType(2321 'list item that has nested items',2322 '[<listItem listIndent="0" listType="numbered">a</listItem>]' +2323 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2324 '<listItem listIndent="1" listType="bulleted">c</listItem>',2325 '<ul>' +2326 '<li>' +2327 'a' +2328 '<ul>' +2329 '<li>b</li>' +2330 '<li>c</li>' +2331 '</ul>' +2332 '</li>' +2333 '</ul>'2334 );2335 // The change will be "prevented" by post fixer.2336 testChangeType(2337 'list item that is a nested item',2338 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2339 '<listItem listIndent="1" listType="numbered">b</listItem>' +2340 '[<listItem listIndent="1" listType="numbered">c</listItem>]' +2341 '<listItem listIndent="1" listType="numbered">d</listItem>',2342 '<ul>' +2343 '<li>' +2344 'a' +2345 '<ol>' +2346 '<li>b</li>' +2347 '<li>c</li>' +2348 '<li>d</li>' +2349 '</ol>' +2350 '</li>' +2351 '</ul>'2352 );2353 } );2354 describe( 'change indent', () => {2355 describe( 'same list type', () => {2356 testChangeIndent(2357 'indent last item of flat list', 1,2358 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2359 '[<listItem listIndent="0" listType="bulleted">b</listItem>]',2360 '<ul>' +2361 '<li>' +2362 'a' +2363 '<ul>' +2364 '<li>b</li>' +2365 '</ul>' +2366 '</li>' +2367 '</ul>'2368 );2369 testChangeIndent(2370 'indent middle item of flat list', 1,2371 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2372 '[<listItem listIndent="0" listType="bulleted">b</listItem>]' +2373 '<listItem listIndent="0" listType="bulleted">c</listItem>',2374 '<ul>' +2375 '<li>' +2376 'a' +2377 '<ul>' +2378 '<li>b</li>' +2379 '</ul>' +2380 '</li>' +2381 '<li>c</li>' +2382 '</ul>'2383 );2384 testChangeIndent(2385 'indent last item in nested list', 2,2386 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2387 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2388 '[<listItem listIndent="1" listType="bulleted">c</listItem>]',2389 '<ul>' +2390 '<li>' +2391 'a' +2392 '<ul>' +2393 '<li>' +2394 'b' +2395 '<ul>' +2396 '<li>c</li>' +2397 '</ul>' +2398 '</li>' +2399 '</ul>' +2400 '</li>' +2401 '</ul>'2402 );2403 testChangeIndent(2404 'indent middle item in nested list', 2,2405 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2406 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2407 '[<listItem listIndent="1" listType="bulleted">c</listItem>]' +2408 '<listItem listIndent="1" listType="bulleted">d</listItem>',2409 '<ul>' +2410 '<li>' +2411 'a' +2412 '<ul>' +2413 '<li>' +2414 'b' +2415 '<ul>' +2416 '<li>c</li>' +2417 '</ul>' +2418 '</li>' +2419 '<li>d</li>' +2420 '</ul>' +2421 '</li>' +2422 '</ul>'2423 );2424 // Keep in mind that this test is different than "executing command on item that has nested list".2425 // A command is automatically indenting nested items so the hierarchy is preserved.2426 // Here we test conversion and the change is simple changing indent of one item.2427 // This may be true also for other tests in this suite, keep this in mind.2428 testChangeIndent(2429 'indent item that has nested list', 1,2430 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2431 '[<listItem listIndent="0" listType="bulleted">b</listItem>]' +2432 '<listItem listIndent="1" listType="bulleted">c</listItem>',2433 '<ul>' +2434 '<li>' +2435 'a' +2436 '<ul>' +2437 '<li>b</li>' +2438 '<li>c</li>' +2439 '</ul>' +2440 '</li>' +2441 '</ul>'2442 );2443 testChangeIndent(2444 'indent item that in view is a next sibling of item that has nested list', 1,2445 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2446 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2447 '[<listItem listIndent="0" listType="bulleted">c</listItem>]' +2448 '<listItem listIndent="1" listType="bulleted">d</listItem>',2449 '<ul>' +2450 '<li>' +2451 'a' +2452 '<ul>' +2453 '<li>b</li>' +2454 '<li>c</li>' +2455 '<li>d</li>' +2456 '</ul>' +2457 '</li>' +2458 '</ul>'2459 );2460 testChangeIndent(2461 'outdent the first item of nested list', 0,2462 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2463 '[<listItem listIndent="1" listType="bulleted">b</listItem>]' +2464 '<listItem listIndent="1" listType="bulleted">c</listItem>' +2465 '<listItem listIndent="1" listType="bulleted">d</listItem>',2466 '<ul>' +2467 '<li>a</li>' +2468 '<li>' +2469 'b' +2470 '<ul>' +2471 '<li>c</li>' +2472 '<li>d</li>' +2473 '</ul>' +2474 '</li>' +2475 '</ul>'2476 );2477 testChangeIndent(2478 'outdent item from the middle of nested list', 0,2479 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2480 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2481 '[<listItem listIndent="1" listType="bulleted">c</listItem>]' +2482 '<listItem listIndent="1" listType="bulleted">d</listItem>',2483 '<ul>' +2484 '<li>' +2485 'a' +2486 '<ul>' +2487 '<li>b</li>' +2488 '</ul>' +2489 '</li>' +2490 '<li>' +2491 'c' +2492 '<ul>' +2493 '<li>d</li>' +2494 '</ul>' +2495 '</li>' +2496 '</ul>'2497 );2498 testChangeIndent(2499 'outdent the last item of nested list', 0,2500 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2501 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2502 '[<listItem listIndent="1" listType="bulleted">c</listItem>]',2503 '<ul>' +2504 '<li>' +2505 'a' +2506 '<ul>' +2507 '<li>b</li>' +2508 '</ul>' +2509 '</li>' +2510 '<li>c</li>' +2511 '</ul>'2512 );2513 testChangeIndent(2514 'outdent the only item of nested list', 1,2515 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2516 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2517 '[<listItem listIndent="2" listType="bulleted">c</listItem>]' +2518 '<listItem listIndent="1" listType="bulleted">d</listItem>',2519 '<ul>' +2520 '<li>' +2521 'a' +2522 '<ul>' +2523 '<li>b</li>' +2524 '<li>c</li>' +2525 '<li>d</li>' +2526 '</ul>' +2527 '</li>' +2528 '</ul>'2529 );2530 testChangeIndent(2531 'outdent item by two', 0,2532 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2533 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2534 '[<listItem listIndent="2" listType="bulleted">c</listItem>]' +2535 '<listItem listIndent="0" listType="bulleted">d</listItem>',2536 '<ul>' +2537 '<li>' +2538 'a' +2539 '<ul>' +2540 '<li>b</li>' +2541 '</ul>' +2542 '</li>' +2543 '<li>c</li>' +2544 '<li>d</li>' +2545 '</ul>'2546 );2547 } );2548 describe( 'different list type', () => {2549 testChangeIndent(2550 'indent middle item of flat list', 1,2551 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2552 '[<listItem listIndent="0" listType="numbered">b</listItem>]' +2553 '<listItem listIndent="0" listType="bulleted">c</listItem>',2554 '<ul>' +2555 '<li>' +2556 'a' +2557 '<ol>' +2558 '<li>b</li>' +2559 '</ol>' +2560 '</li>' +2561 '<li>c</li>' +2562 '</ul>'2563 );2564 testChangeIndent(2565 'indent item that has nested list', 1,2566 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2567 '[<listItem listIndent="0" listType="numbered">b</listItem>]' +2568 '<listItem listIndent="1" listType="bulleted">c</listItem>',2569 '<ul>' +2570 '<li>' +2571 'a' +2572 '<ol>' +2573 '<li>b</li>' +2574 '<li>c</li>' +2575 '</ol>' +2576 '</li>' +2577 '</ul>'2578 );2579 testChangeIndent(2580 'indent item that in view is a next sibling of item that has nested list #1', 1,2581 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2582 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2583 '[<listItem listIndent="0" listType="numbered">c</listItem>]' +2584 '<listItem listIndent="1" listType="bulleted">d</listItem>',2585 '<ul>' +2586 '<li>' +2587 'a' +2588 '<ul>' +2589 '<li>b</li>' +2590 '<li>c</li>' +2591 '<li>d</li>' +2592 '</ul>' +2593 '</li>' +2594 '</ul>'2595 );2596 testChangeIndent(2597 'outdent the first item of nested list', 0,2598 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2599 '[<listItem listIndent="1" listType="bulleted">b</listItem>]' +2600 '<listItem listIndent="1" listType="bulleted">c</listItem>' +2601 '<listItem listIndent="1" listType="bulleted">d</listItem>',2602 '<ul>' +2603 '<li>a</li>' +2604 '<li>' +2605 'b' +2606 '<ul>' +2607 '<li>c</li>' +2608 '<li>d</li>' +2609 '</ul>' +2610 '</li>' +2611 '</ul>'2612 );2613 testChangeIndent(2614 'outdent the only item of nested list', 1,2615 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2616 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2617 '[<listItem listIndent="2" listType="bulleted">c</listItem>]' +2618 '<listItem listIndent="1" listType="bulleted">d</listItem>',2619 '<ul>' +2620 '<li>' +2621 'a' +2622 '<ul>' +2623 '<li>b</li>' +2624 '<li>c</li>' +2625 '<li>d</li>' +2626 '</ul>' +2627 '</li>' +2628 '</ul>'2629 );2630 testChangeIndent(2631 'outdent item by two', 0,2632 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2633 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2634 '[<listItem listIndent="2" listType="numbered">c</listItem>]' +2635 '<listItem listIndent="0" listType="bulleted">d</listItem>',2636 '<ul>' +2637 '<li>' +2638 'a' +2639 '<ul>' +2640 '<li>b</li>' +2641 '</ul>' +2642 '</li>' +2643 '</ul>' +2644 '<ol>' +2645 '<li>c</li>' +2646 '</ol>' +2647 '<ul>' +2648 '<li>d</li>' +2649 '</ul>'2650 );2651 } );2652 } );2653 describe( 'rename from list item', () => {2654 testRenameFromListItem(2655 'rename nested item from the middle #1',2656 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2657 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2658 '[<listItem listIndent="1" listType="bulleted">c</listItem>]' +2659 '<listItem listIndent="1" listType="bulleted">d</listItem>', // This indent should be fixed by post fixer.2660 '<ul>' +2661 '<li>' +2662 'a' +2663 '<ul>' +2664 '<li>b</li>' +2665 '</ul>' +2666 '</li>' +2667 '</ul>' +2668 '<p>c</p>' +2669 '<ul>' +2670 '<li>d</li>' +2671 '</ul>',2672 false2673 );2674 testRenameFromListItem(2675 'rename nested item from the middle #2 - nightmare example',2676 // Indents in this example should be fixed by post fixer.2677 // This nightmare example checks if structure of the list is kept as intact as possible.2678 '<listItem listIndent="0" listType="bulleted">a</listItem>' + // a -------- --> a --------2679 '<listItem listIndent="1" listType="bulleted">b</listItem>' + // b -------- --> b --------2680 '[<listItem listIndent="2" listType="bulleted">c</listItem>]' + // c -------- --> --------2681 '<listItem listIndent="3" listType="bulleted">d</listItem>' + // d -------- --> d --------2682 '<listItem listIndent="3" listType="bulleted">e</listItem>' + // e -------- --> e --------2683 '<listItem listIndent="4" listType="bulleted">f</listItem>' + // f -------- --> f --------2684 '<listItem listIndent="2" listType="bulleted">g</listItem>' + // g -------- --> g --------2685 '<listItem listIndent="3" listType="bulleted">h</listItem>' + // h -------- --> h --------2686 '<listItem listIndent="4" listType="bulleted">i</listItem>' + // i -------- --> i --------2687 '<listItem listIndent="1" listType="bulleted">j</listItem>' + // j -------- --> j --------2688 '<listItem listIndent="2" listType="bulleted">k</listItem>' + // k -------- --> k --------2689 '<listItem listIndent="0" listType="bulleted">l</listItem>' + // l -------- --> l --------2690 '<listItem listIndent="1" listType="bulleted">m</listItem>', // m -------- --> m --------2691 '<ul>' +2692 '<li>' +2693 'a' +2694 '<ul>' +2695 '<li>b</li>' +2696 '</ul>' +2697 '</li>' +2698 '</ul>' +2699 '<p>c</p>' +2700 '<ul>' +2701 '<li>d</li>' +2702 '<li>' +2703 'e' +2704 '<ul>' +2705 '<li>f</li>' +2706 '</ul>' +2707 '</li>' +2708 '<li>' +2709 'g' +2710 '<ul>' +2711 '<li>' +2712 'h' +2713 '<ul>' +2714 '<li>i</li>' +2715 '</ul>' +2716 '</li>' +2717 '</ul>' +2718 '</li>' +2719 '<li>' +2720 'j' +2721 '<ul>' +2722 '<li>k</li>' +2723 '</ul>' +2724 '</li>' +2725 '<li>' +2726 'l' +2727 '<ul>' +2728 '<li>m</li>' +2729 '</ul>' +2730 '</li>' +2731 '</ul>',2732 false2733 );2734 testRenameFromListItem(2735 'rename nested item from the middle #3 - manual test example',2736 // Indents in this example should be fixed by post fixer.2737 // This example checks a bug found by testing manual test.2738 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2739 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2740 '[<listItem listIndent="2" listType="bulleted">c</listItem>]' +2741 '<listItem listIndent="1" listType="bulleted">d</listItem>' +2742 '<listItem listIndent="2" listType="bulleted">e</listItem>' +2743 '<listItem listIndent="2" listType="bulleted">f</listItem>' +2744 '<listItem listIndent="2" listType="bulleted">g</listItem>' +2745 '<listItem listIndent="2" listType="bulleted">h</listItem>' +2746 '<listItem listIndent="0" listType="bulleted"></listItem>' +2747 '<listItem listIndent="1" listType="bulleted"></listItem>' +2748 '<listItem listIndent="2" listType="numbered">k</listItem>' +2749 '<listItem listIndent="2" listType="numbered">l</listItem>',2750 '<ul>' +2751 '<li>' +2752 'a' +2753 '<ul>' +2754 '<li>b</li>' +2755 '</ul>' +2756 '</li>' +2757 '</ul>' +2758 '<p>c</p>' +2759 '<ul>' +2760 '<li>' +2761 'd' +2762 '<ul>' +2763 '<li>e</li>' +2764 '<li>f</li>' +2765 '<li>g</li>' +2766 '<li>h</li>' +2767 '</ul>' +2768 '</li>' +2769 '<li>' +2770 '<ul>' +2771 '<li>' +2772 '<ol>' +2773 '<li>k</li>' +2774 '<li>l</li>' +2775 '</ol>' +2776 '</li>' +2777 '</ul>' +2778 '</li>' +2779 '</ul>',2780 false2781 );2782 testRenameFromListItem(2783 'rename the only nested item',2784 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2785 '[<listItem listIndent="1" listType="bulleted">b</listItem>]',2786 '<ul>' +2787 '<li>a</li>' +2788 '</ul>' +2789 '<p>b</p>'2790 );2791 } );2792 describe( 'rename to list item (with attribute change)', () => {2793 testRenameToListItem(2794 'element into first item in nested list', 1,2795 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2796 '[<paragraph>b</paragraph>]',2797 '<ul>' +2798 '<li>' +2799 'a' +2800 '<ul>' +2801 '<li>b</li>' +2802 '</ul>' +2803 '</li>' +2804 '</ul>'2805 );2806 testRenameToListItem(2807 'element into last item in nested list', 1,2808 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2809 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2810 '[<paragraph>c</paragraph>]',2811 '<ul>' +2812 '<li>' +2813 'a' +2814 '<ul>' +2815 '<li>b</li>' +2816 '<li>c</li>' +2817 '</ul>' +2818 '</li>' +2819 '</ul>'2820 );2821 testRenameToListItem(2822 'element into a first item in deeply nested list', 2,2823 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2824 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2825 '[<paragraph>c</paragraph>]' +2826 '<listItem listIndent="0" listType="bulleted">d</listItem>',2827 '<ul>' +2828 '<li>' +2829 'a' +2830 '<ul>' +2831 '<li>' +2832 'b' +2833 '<ul>' +2834 '<li>c</li>' +2835 '</ul>' +2836 '</li>' +2837 '</ul>' +2838 '</li>' +2839 '<li>d</li>' +2840 '</ul>'2841 );2842 } );2843 describe( 'move', () => {2844 // Since move is in fact remove + insert and does not event have its own converter, only a few cases will be tested here.2845 testMove(2846 'out nested list items',2847 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2848 '[<listItem listIndent="1" listType="bulleted">b</listItem>' +2849 '<listItem listIndent="2" listType="bulleted">c</listItem>]' +2850 '<listItem listIndent="3" listType="bulleted">d</listItem>' + // This indent should be fixed by post fixer.2851 '<listItem listIndent="4" listType="bulleted">e</listItem>' + // This indent should be fixed by post fixer.2852 '<paragraph>x</paragraph>',2853 6,2854 '<ul>' +2855 '<li>' +2856 'a' +2857 '<ul>' +2858 '<li>' +2859 'd' +2860 '<ul>' +2861 '<li>e</li>' +2862 '</ul>' +2863 '</li>' +2864 '</ul>' +2865 '</li>' +2866 '</ul>' +2867 '<p>x</p>' +2868 '<ul>' +2869 '<li>' +2870 'b' +2871 '<ul>' +2872 '<li>c</li>' +2873 '</ul>' +2874 '</li>' +2875 '</ul>'2876 );2877 testMove(2878 'nested list items between lists of same type',2879 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2880 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2881 '[<listItem listIndent="2" listType="bulleted">c</listItem>' +2882 '<listItem listIndent="3" listType="bulleted">d</listItem>]' +2883 '<listItem listIndent="4" listType="bulleted">e</listItem>' +2884 '<paragraph>x</paragraph>' +2885 '<listItem listIndent="0" listType="bulleted">f</listItem>' +2886 '<listItem listIndent="0" listType="bulleted">g</listItem>',2887 7,2888 '<ul>' +2889 '<li>' +2890 'a' +2891 '<ul>' +2892 '<li>' +2893 'b' +2894 '<ul>' +2895 '<li>e</li>' +2896 '</ul>' +2897 '</li>' +2898 '</ul>' +2899 '</li>' +2900 '</ul>' +2901 '<p>x</p>' +2902 '<ul>' +2903 '<li>' +2904 'f' +2905 '<ul>' +2906 '<li>' +2907 'c' +2908 '<ul>' +2909 '<li>d</li>' +2910 '</ul>' +2911 '</li>' +2912 '</ul>' +2913 '</li>' +2914 '<li>g</li>' +2915 '</ul>'2916 );2917 testMove(2918 'nested list items between lists of different type',2919 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2920 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2921 '[<listItem listIndent="2" listType="bulleted">c</listItem>' +2922 '<listItem listIndent="3" listType="bulleted">d</listItem>]' +2923 '<listItem listIndent="4" listType="bulleted">e</listItem>' +2924 '<paragraph>x</paragraph>' +2925 '<listItem listIndent="0" listType="numbered">f</listItem>' +2926 '<listItem listIndent="1" listType="numbered">g</listItem>',2927 7,2928 '<ul>' +2929 '<li>' +2930 'a' +2931 '<ul>' +2932 '<li>' +2933 'b' +2934 '<ul>' +2935 '<li>e</li>' +2936 '</ul>' +2937 '</li>' +2938 '</ul>' +2939 '</li>' +2940 '</ul>' +2941 '<p>x</p>' +2942 '<ol>' +2943 '<li>' +2944 'f' +2945 '<ul>' +2946 '<li>' +2947 'c' +2948 '<ul>' +2949 '<li>d</li>' +2950 '</ul>' +2951 '</li>' +2952 '<li>g</li>' +2953 '</ul>' +2954 '</li>' +2955 '</ol>',2956 false2957 );2958 testMove(2959 'element between nested list',2960 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2961 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2962 '<listItem listIndent="2" listType="bulleted">c</listItem>' +2963 '<listItem listIndent="3" listType="bulleted">d</listItem>' +2964 '[<paragraph>x</paragraph>]',2965 2,2966 '<ul>' +2967 '<li>' +2968 'a' +2969 '<ul>' +2970 '<li>b</li>' +2971 '</ul>' +2972 '</li>' +2973 '</ul>' +2974 '<p>x</p>' +2975 '<ul>' +2976 '<li>' +2977 'c' +2978 '<ul>' +2979 '<li>d</li>' +2980 '</ul>' +2981 '</li>' +2982 '</ul>',2983 false2984 );2985 testMove(2986 'multiple nested list items of different types #1 - fix at start',2987 '<listItem listIndent="0" listType="bulleted">a</listItem>' +2988 '<listItem listIndent="1" listType="bulleted">b</listItem>' +2989 '[<listItem listIndent="1" listType="bulleted">c</listItem>' +2990 '<listItem listIndent="0" listType="bulleted">d</listItem>' +2991 '<listItem listIndent="1" listType="numbered">e</listItem>]' +2992 '<listItem listIndent="1" listType="numbered">f</listItem>' +2993 '<listItem listIndent="0" listType="bulleted">g</listItem>' +2994 '<listItem listIndent="1" listType="numbered">h</listItem>' +2995 '<listItem listIndent="1" listType="numbered">i</listItem>',2996 8,2997 '<ul>' +2998 '<li>' +2999 'a' +3000 '<ul>' +3001 '<li>b</li>' +3002 '<li>f</li>' +3003 '</ul>' +3004 '</li>' +3005 '<li>' +3006 'g' +3007 '<ol>' +3008 '<li>h</li>' +3009 '<li>c</li>' +3010 '</ol>' +3011 '</li>' +3012 '<li>' +3013 'd' +3014 '<ol>' +3015 '<li>e</li>' +3016 '<li>i</li>' +3017 '</ol>' +3018 '</li>' +3019 '</ul>'3020 );3021 testMove(3022 'multiple nested list items of different types #2 - fix at end',3023 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3024 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3025 '[<listItem listIndent="1" listType="bulleted">c</listItem>' +3026 '<listItem listIndent="0" listType="bulleted">d</listItem>' +3027 '<listItem listIndent="1" listType="numbered">e</listItem>]' +3028 '<listItem listIndent="1" listType="numbered">f</listItem>' +3029 '<listItem listIndent="0" listType="bulleted">g</listItem>' +3030 '<listItem listIndent="1" listType="bulleted">h</listItem>' +3031 '<listItem listIndent="1" listType="bulleted">i</listItem>',3032 8,3033 '<ul>' +3034 '<li>' +3035 'a' +3036 '<ul>' +3037 '<li>b</li>' +3038 '<li>f</li>' +3039 '</ul>' +3040 '</li>' +3041 '<li>' +3042 'g' +3043 '<ul>' +3044 '<li>h</li>' +3045 '<li>c</li>' +3046 '</ul>' +3047 '</li>' +3048 '<li>' +3049 'd' +3050 '<ol>' +3051 '<li>e</li>' +3052 '<li>i</li>' +3053 '</ol>' +3054 '</li>' +3055 '</ul>'3056 );3057 } );3058 } );3059 } );3060 describe( 'post fixer', () => {3061 describe( 'insert', () => {3062 function testList( input, inserted, output ) {3063 return () => {3064 // Wrap all changes in one block to avoid post-fixing the selection3065 // (which may be incorret) in the meantime.3066 model.change( () => {3067 setModelData( model, input );3068 model.change( writer => {3069 writer.insert( parseModel( inserted, model.schema ), modelDoc.selection.getFirstPosition() );3070 } );3071 } );3072 expect( getModelData( model, { withoutSelection: true } ) ).to.equal( output );3073 };3074 }3075 it( 'element before nested list', testList(3076 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3077 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3078 '[]' +3079 '<listItem listIndent="2" listType="bulleted">d</listItem>' +3080 '<listItem listIndent="2" listType="bulleted">e</listItem>' +3081 '<listItem listIndent="3" listType="bulleted">f</listItem>',3082 '<paragraph>x</paragraph>',3083 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3084 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3085 '<paragraph>x</paragraph>' +3086 '<listItem listIndent="0" listType="bulleted">d</listItem>' +3087 '<listItem listIndent="0" listType="bulleted">e</listItem>' +3088 '<listItem listIndent="1" listType="bulleted">f</listItem>'3089 ) );3090 it( 'list item before nested list', testList(3091 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3092 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3093 '[]' +3094 '<listItem listIndent="2" listType="bulleted">d</listItem>' +3095 '<listItem listIndent="2" listType="bulleted">e</listItem>' +3096 '<listItem listIndent="3" listType="bulleted">f</listItem>',3097 '<listItem listIndent="0" listType="bulleted">x</listItem>',3098 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3099 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3100 '<listItem listIndent="0" listType="bulleted">x</listItem>' +3101 '<listItem listIndent="1" listType="bulleted">d</listItem>' +3102 '<listItem listIndent="1" listType="bulleted">e</listItem>' +3103 '<listItem listIndent="2" listType="bulleted">f</listItem>'3104 ) );3105 it( 'multiple list items with too big indent', testList(3106 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3107 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3108 '[]' +3109 '<listItem listIndent="1" listType="bulleted">c</listItem>',3110 '<listItem listIndent="4" listType="bulleted">x</listItem>' +3111 '<listItem listIndent="5" listType="bulleted">x</listItem>' +3112 '<listItem listIndent="4" listType="bulleted">x</listItem>',3113 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3114 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3115 '<listItem listIndent="2" listType="bulleted">x</listItem>' +3116 '<listItem listIndent="3" listType="bulleted">x</listItem>' +3117 '<listItem listIndent="2" listType="bulleted">x</listItem>' +3118 '<listItem listIndent="1" listType="bulleted">c</listItem>'3119 ) );3120 it( 'item with different type - top level list', testList(3121 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3122 '<listItem listIndent="0" listType="bulleted">b</listItem>' +3123 '[]' +3124 '<listItem listIndent="0" listType="bulleted">c</listItem>',3125 '<listItem listIndent="0" listType="numbered">x</listItem>',3126 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3127 '<listItem listIndent="0" listType="bulleted">b</listItem>' +3128 '<listItem listIndent="0" listType="numbered">x</listItem>' +3129 '<listItem listIndent="0" listType="bulleted">c</listItem>'3130 ) );3131 it( 'multiple items with different type - nested list', testList(3132 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3133 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3134 '[]' +3135 '<listItem listIndent="2" listType="bulleted">c</listItem>',3136 '<listItem listIndent="1" listType="numbered">x</listItem>' +3137 '<listItem listIndent="2" listType="numbered">x</listItem>',3138 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3139 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3140 '<listItem listIndent="1" listType="bulleted">x</listItem>' +3141 '<listItem listIndent="2" listType="numbered">x</listItem>' +3142 '<listItem listIndent="2" listType="numbered">c</listItem>'3143 ) );3144 it( 'item with different type, in nested list, after nested list', testList(3145 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3146 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3147 '<listItem listIndent="2" listType="bulleted">c</listItem>' +3148 '[]',3149 '<listItem listIndent="1" listType="numbered">x</listItem>',3150 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3151 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3152 '<listItem listIndent="2" listType="bulleted">c</listItem>' +3153 '<listItem listIndent="1" listType="bulleted">x</listItem>'3154 ) );3155 it( 'two list items with mismatched types inserted in one batch', () => {3156 const input =3157 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3158 '<listItem listIndent="1" listType="bulleted">b</listItem>';3159 const output =3160 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3161 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3162 '<listItem listIndent="1" listType="bulleted">c</listItem>' +3163 '<listItem listIndent="1" listType="bulleted">d</listItem>';3164 setModelData( model, input );3165 const item1 = '<listItem listIndent="1" listType="numbered">c</listItem>';3166 const item2 = '<listItem listIndent="1" listType="bulleted">d</listItem>';3167 model.change( writer => {3168 writer.append( parseModel( item1, model.schema ), modelRoot );3169 writer.append( parseModel( item2, model.schema ), modelRoot );3170 } );3171 expect( getModelData( model, { withoutSelection: true } ) ).to.equal( output );3172 } );3173 } );3174 describe( 'remove', () => {3175 function testList( input, output ) {3176 return () => {3177 model.change( writer => {3178 setModelData( model, input );3179 writer.remove( modelDoc.selection.getFirstRange() );3180 } );3181 expect( getModelData( model, { withoutSelection: true } ) ).to.equal( output );3182 };3183 }3184 it( 'first list item', testList(3185 '[<listItem listIndent="0" listType="bulleted">a</listItem>]' +3186 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3187 '<listItem listIndent="2" listType="bulleted">c</listItem>',3188 '<listItem listIndent="0" listType="bulleted">b</listItem>' +3189 '<listItem listIndent="1" listType="bulleted">c</listItem>'3190 ) );3191 it( 'first list item of nested list', testList(3192 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3193 '[<listItem listIndent="1" listType="bulleted">b</listItem>]' +3194 '<listItem listIndent="2" listType="bulleted">c</listItem>' +3195 '<listItem listIndent="3" listType="bulleted">d</listItem>' +3196 '<listItem listIndent="1" listType="bulleted">e</listItem>' +3197 '<listItem listIndent="2" listType="bulleted">f</listItem>',3198 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3199 '<listItem listIndent="1" listType="bulleted">c</listItem>' +3200 '<listItem listIndent="2" listType="bulleted">d</listItem>' +3201 '<listItem listIndent="1" listType="bulleted">e</listItem>' +3202 '<listItem listIndent="2" listType="bulleted">f</listItem>'3203 ) );3204 it( 'selection over two different nested lists of same indent', testList(3205 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3206 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3207 '[<listItem listIndent="1" listType="bulleted">c</listItem>' +3208 '<listItem listIndent="0" listType="bulleted">d</listItem>' +3209 '<listItem listIndent="1" listType="numbered">e</listItem>]' +3210 '<listItem listIndent="1" listType="numbered">f</listItem>',3211 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3212 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3213 '<listItem listIndent="1" listType="bulleted">f</listItem>'3214 ) );3215 } );3216 describe( 'move', () => {3217 function testList( input, offset, output ) {3218 return () => {3219 model.change( writer => {3220 setModelData( model, input );3221 const targetPosition = writer.createPositionAt( modelRoot, offset );3222 writer.move( modelDoc.selection.getFirstRange(), targetPosition );3223 } );3224 expect( getModelData( model, { withoutSelection: true } ) ).to.equal( output );3225 };3226 }3227 it( 'nested list item out of list structure', testList(3228 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3229 '[<listItem listIndent="1" listType="bulleted">b</listItem>' +3230 '<listItem listIndent="2" listType="bulleted">c</listItem>]' +3231 '<listItem listIndent="3" listType="bulleted">d</listItem>' +3232 '<listItem listIndent="4" listType="bulleted">e</listItem>' +3233 '<paragraph>x</paragraph>',3234 6,3235 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3236 '<listItem listIndent="1" listType="bulleted">d</listItem>' +3237 '<listItem listIndent="2" listType="bulleted">e</listItem>' +3238 '<paragraph>x</paragraph>' +3239 '<listItem listIndent="0" listType="bulleted">b</listItem>' +3240 '<listItem listIndent="1" listType="bulleted">c</listItem>'3241 ) );3242 it( 'list items between lists', testList(3243 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3244 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3245 '[<listItem listIndent="2" listType="bulleted">c</listItem>' +3246 '<listItem listIndent="3" listType="bulleted">d</listItem>]' +3247 '<listItem listIndent="4" listType="bulleted">e</listItem>' +3248 '<paragraph>x</paragraph>' +3249 '<listItem listIndent="0" listType="bulleted">f</listItem>' +3250 '<listItem listIndent="0" listType="bulleted">g</listItem>',3251 7,3252 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3253 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3254 '<listItem listIndent="2" listType="bulleted">e</listItem>' +3255 '<paragraph>x</paragraph>' +3256 '<listItem listIndent="0" listType="bulleted">f</listItem>' +3257 '<listItem listIndent="1" listType="bulleted">c</listItem>' +3258 '<listItem listIndent="2" listType="bulleted">d</listItem>' +3259 '<listItem listIndent="0" listType="bulleted">g</listItem>'3260 ) );3261 it( 'element in between nested list items', testList(3262 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3263 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3264 '<listItem listIndent="2" listType="bulleted">c</listItem>' +3265 '<listItem listIndent="3" listType="bulleted">d</listItem>' +3266 '[<paragraph>x</paragraph>]',3267 2,3268 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3269 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3270 '<paragraph>x</paragraph>' +3271 '<listItem listIndent="0" listType="bulleted">c</listItem>' +3272 '<listItem listIndent="1" listType="bulleted">d</listItem>'3273 ) );3274 it( 'multiple nested list items of different types #1 - fix at start', testList(3275 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3276 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3277 '[<listItem listIndent="1" listType="bulleted">c</listItem>' +3278 '<listItem listIndent="0" listType="bulleted">d</listItem>' +3279 '<listItem listIndent="1" listType="numbered">e</listItem>]' +3280 '<listItem listIndent="1" listType="numbered">f</listItem>' +3281 '<listItem listIndent="0" listType="bulleted">g</listItem>' +3282 '<listItem listIndent="1" listType="numbered">h</listItem>' +3283 '<listItem listIndent="1" listType="numbered">i</listItem>',3284 8,3285 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3286 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3287 '<listItem listIndent="1" listType="bulleted">f</listItem>' +3288 '<listItem listIndent="0" listType="bulleted">g</listItem>' +3289 '<listItem listIndent="1" listType="numbered">h</listItem>' +3290 '<listItem listIndent="1" listType="numbered">c</listItem>' +3291 '<listItem listIndent="0" listType="bulleted">d</listItem>' +3292 '<listItem listIndent="1" listType="numbered">e</listItem>' +3293 '<listItem listIndent="1" listType="numbered">i</listItem>'3294 ) );3295 it( 'multiple nested list items of different types #2 - fix at end', testList(3296 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3297 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3298 '[<listItem listIndent="1" listType="bulleted">c</listItem>' +3299 '<listItem listIndent="0" listType="bulleted">d</listItem>' +3300 '<listItem listIndent="1" listType="numbered">e</listItem>]' +3301 '<listItem listIndent="1" listType="numbered">f</listItem>' +3302 '<listItem listIndent="0" listType="bulleted">g</listItem>' +3303 '<listItem listIndent="1" listType="bulleted">h</listItem>' +3304 '<listItem listIndent="1" listType="bulleted">i</listItem>',3305 8,3306 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3307 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3308 '<listItem listIndent="1" listType="bulleted">f</listItem>' +3309 '<listItem listIndent="0" listType="bulleted">g</listItem>' +3310 '<listItem listIndent="1" listType="bulleted">h</listItem>' +3311 '<listItem listIndent="1" listType="bulleted">c</listItem>' +3312 '<listItem listIndent="0" listType="bulleted">d</listItem>' +3313 '<listItem listIndent="1" listType="numbered">e</listItem>' +3314 '<listItem listIndent="1" listType="numbered">i</listItem>'3315 ) );3316 // #78.3317 it( 'move out of container', testList(3318 '<blockQuote>' +3319 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3320 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3321 '<listItem listIndent="1" listType="bulleted">c</listItem>' +3322 '<listItem listIndent="1" listType="bulleted">d</listItem>' +3323 '[<listItem listIndent="2" listType="bulleted">e</listItem>]' +3324 '</blockQuote>',3325 0,3326 '<listItem listIndent="0" listType="bulleted">e</listItem>' +3327 '<blockQuote>' +3328 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3329 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3330 '<listItem listIndent="1" listType="bulleted">c</listItem>' +3331 '<listItem listIndent="1" listType="bulleted">d</listItem>' +3332 '</blockQuote>'3333 ) );3334 } );3335 describe( 'rename', () => {3336 it( 'rename nested item', () => {3337 const modelBefore =3338 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3339 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3340 '[<listItem listIndent="2" listType="bulleted">c</listItem>]' +3341 '<listItem listIndent="2" listType="bulleted">d</listItem>' +3342 '<listItem listIndent="3" listType="bulleted">e</listItem>' +3343 '<listItem listIndent="1" listType="bulleted">f</listItem>' +3344 '<listItem listIndent="2" listType="bulleted">g</listItem>' +3345 '<listItem listIndent="1" listType="bulleted">h</listItem>' +3346 '<listItem listIndent="2" listType="bulleted">i</listItem>';3347 const expectedModel =3348 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3349 '<listItem listIndent="1" listType="bulleted">b</listItem>' +3350 '<paragraph>c</paragraph>' +3351 '<listItem listIndent="0" listType="bulleted">d</listItem>' +3352 '<listItem listIndent="1" listType="bulleted">e</listItem>' +3353 '<listItem listIndent="0" listType="bulleted">f</listItem>' +3354 '<listItem listIndent="1" listType="bulleted">g</listItem>' +3355 '<listItem listIndent="0" listType="bulleted">h</listItem>' +3356 '<listItem listIndent="1" listType="bulleted">i</listItem>';3357 model.change( writer => {3358 setModelData( model, modelBefore );3359 const element = modelDoc.selection.getFirstPosition().nodeAfter;3360 writer.rename( element, 'paragraph' );3361 } );3362 expect( getModelData( model, { withoutSelection: true } ) ).to.equal( expectedModel );3363 } );3364 } );3365 } );3366 describe( 'paste and insertContent integration', () => {3367 it( 'should be triggered on DataController#insertContent()', () => {3368 setModelData( model,3369 '<listItem listType="bulleted" listIndent="0">A</listItem>' +3370 '<listItem listType="bulleted" listIndent="1">B[]</listItem>' +3371 '<listItem listType="bulleted" listIndent="2">C</listItem>'3372 );3373 editor.model.insertContent(3374 parseModel(3375 '<listItem listType="bulleted" listIndent="0">X</listItem>' +3376 '<listItem listType="bulleted" listIndent="1">Y</listItem>',3377 model.schema3378 )3379 );3380 expect( getModelData( model ) ).to.equal(3381 '<listItem listIndent="0" listType="bulleted">A</listItem>' +3382 '<listItem listIndent="1" listType="bulleted">BX</listItem>' +3383 '<listItem listIndent="2" listType="bulleted">Y[]</listItem>' +3384 '<listItem listIndent="2" listType="bulleted">C</listItem>'3385 );3386 } );3387 it( 'should be triggered when selectable is passed', () => {3388 setModelData( model,3389 '<listItem listType="bulleted" listIndent="0">A</listItem>' +3390 '<listItem listType="bulleted" listIndent="1">B[]</listItem>' +3391 '<listItem listType="bulleted" listIndent="2">C</listItem>'3392 );3393 model.insertContent(3394 parseModel(3395 '<listItem listType="bulleted" listIndent="0">X</listItem>' +3396 '<listItem listType="bulleted" listIndent="1">Y</listItem>',3397 model.schema3398 ),3399 model.createRange(3400 model.createPositionFromPath( modelRoot, [ 1, 1 ] ),3401 model.createPositionFromPath( modelRoot, [ 1, 1 ] )3402 )3403 );3404 expect( getModelData( model ) ).to.equal(3405 '<listItem listIndent="0" listType="bulleted">A</listItem>' +3406 '<listItem listIndent="1" listType="bulleted">B[]X</listItem>' +3407 '<listItem listIndent="2" listType="bulleted">Y</listItem>' +3408 '<listItem listIndent="2" listType="bulleted">C</listItem>'3409 );3410 } );3411 // Just checking that it doesn't crash. #693412 it( 'should work if an element is passed to DataController#insertContent()', () => {3413 setModelData( model,3414 '<listItem listType="bulleted" listIndent="0">A</listItem>' +3415 '<listItem listType="bulleted" listIndent="1">B[]</listItem>' +3416 '<listItem listType="bulleted" listIndent="2">C</listItem>'3417 );3418 model.change( writer => {3419 const listItem = writer.createElement( 'listItem', { listType: 'bulleted', listIndent: '0' } );3420 writer.insertText( 'X', listItem );3421 model.insertContent( listItem );3422 } );3423 expect( getModelData( model ) ).to.equal(3424 '<listItem listIndent="0" listType="bulleted">A</listItem>' +3425 '<listItem listIndent="1" listType="bulleted">BX[]</listItem>' +3426 '<listItem listIndent="2" listType="bulleted">C</listItem>'3427 );3428 } );3429 // Just checking that it doesn't crash. #693430 it( 'should work if an element is passed to DataController#insertContent() - case #69', () => {3431 setModelData( model,3432 '<listItem listType="bulleted" listIndent="0">A</listItem>' +3433 '<listItem listType="bulleted" listIndent="1">B[]</listItem>' +3434 '<listItem listType="bulleted" listIndent="2">C</listItem>'3435 );3436 model.change( writer => {3437 model.insertContent( writer.createText( 'X' ) );3438 } );3439 expect( getModelData( model ) ).to.equal(3440 '<listItem listIndent="0" listType="bulleted">A</listItem>' +3441 '<listItem listIndent="1" listType="bulleted">BX[]</listItem>' +3442 '<listItem listIndent="2" listType="bulleted">C</listItem>'3443 );3444 } );3445 it( 'should fix indents of pasted list items', () => {3446 setModelData( model,3447 '<listItem listType="bulleted" listIndent="0">A</listItem>' +3448 '<listItem listType="bulleted" listIndent="1">B[]</listItem>' +3449 '<listItem listType="bulleted" listIndent="2">C</listItem>'3450 );3451 const clipboard = editor.plugins.get( 'Clipboard' );3452 clipboard.fire( 'inputTransformation', {3453 content: parseView( '<ul><li>X<ul><li>Y</li></ul></li></ul>' )3454 } );3455 expect( getModelData( model ) ).to.equal(3456 '<listItem listIndent="0" listType="bulleted">A</listItem>' +3457 '<listItem listIndent="1" listType="bulleted">BX</listItem>' +3458 '<listItem listIndent="2" listType="bulleted">Y[]</listItem>' +3459 '<listItem listIndent="2" listType="bulleted">C</listItem>'3460 );3461 } );3462 it( 'should not fix indents of list items that are separated by non-list element', () => {3463 setModelData( model,3464 '<listItem listType="bulleted" listIndent="0">A</listItem>' +3465 '<listItem listType="bulleted" listIndent="1">B[]</listItem>' +3466 '<listItem listType="bulleted" listIndent="2">C</listItem>'3467 );3468 const clipboard = editor.plugins.get( 'Clipboard' );3469 clipboard.fire( 'inputTransformation', {3470 content: parseView( '<ul><li>W<ul><li>X</li></ul></li></ul><p>Y</p><ul><li>Z</li></ul>' )3471 } );3472 expect( getModelData( model ) ).to.equal(3473 '<listItem listIndent="0" listType="bulleted">A</listItem>' +3474 '<listItem listIndent="1" listType="bulleted">BW</listItem>' +3475 '<listItem listIndent="2" listType="bulleted">X</listItem>' +3476 '<paragraph>Y</paragraph>' +3477 '<listItem listIndent="0" listType="bulleted">Z[]</listItem>' +3478 '<listItem listIndent="1" listType="bulleted">C</listItem>'3479 );3480 } );3481 it( 'should co-work correctly with post fixer', () => {3482 setModelData( model,3483 '<listItem listType="bulleted" listIndent="0">A</listItem>' +3484 '<listItem listType="bulleted" listIndent="1">B[]</listItem>' +3485 '<listItem listType="bulleted" listIndent="2">C</listItem>'3486 );3487 const clipboard = editor.plugins.get( 'Clipboard' );3488 clipboard.fire( 'inputTransformation', {3489 content: parseView( '<p>X</p><ul><li>Y</li></ul>' )3490 } );3491 expect( getModelData( model ) ).to.equal(3492 '<listItem listIndent="0" listType="bulleted">A</listItem>' +3493 '<listItem listIndent="1" listType="bulleted">BX</listItem>' +3494 '<listItem listIndent="0" listType="bulleted">Y[]</listItem>' +3495 '<listItem listIndent="1" listType="bulleted">C</listItem>'3496 );3497 } );3498 it( 'should work if items are pasted between listItem elements', () => {3499 // Wrap all changes in one block to avoid post-fixing the selection3500 // (which may be incorret) in the meantime.3501 model.change( () => {3502 setModelData( model,3503 '<listItem listType="bulleted" listIndent="0">A</listItem>' +3504 '<listItem listType="bulleted" listIndent="1">B</listItem>[]' +3505 '<listItem listType="bulleted" listIndent="2">C</listItem>'3506 );3507 const clipboard = editor.plugins.get( 'Clipboard' );3508 clipboard.fire( 'inputTransformation', {3509 content: parseView( '<ul><li>X<ul><li>Y</li></ul></li></ul>' )3510 } );3511 } );3512 expect( getModelData( model ) ).to.equal(3513 '<listItem listIndent="0" listType="bulleted">A</listItem>' +3514 '<listItem listIndent="1" listType="bulleted">B</listItem>' +3515 '<listItem listIndent="1" listType="bulleted">X</listItem>' +3516 '<listItem listIndent="2" listType="bulleted">Y[]</listItem>' +3517 '<listItem listIndent="2" listType="bulleted">C</listItem>'3518 );3519 } );3520 it( 'should create correct model when list items are pasted in top-level list', () => {3521 setModelData( model,3522 '<listItem listType="bulleted" listIndent="0">A[]</listItem>' +3523 '<listItem listType="bulleted" listIndent="1">B</listItem>'3524 );3525 const clipboard = editor.plugins.get( 'Clipboard' );3526 clipboard.fire( 'inputTransformation', {3527 content: parseView( '<ul><li>X<ul><li>Y</li></ul></li></ul>' )3528 } );3529 expect( getModelData( model ) ).to.equal(3530 '<listItem listIndent="0" listType="bulleted">AX</listItem>' +3531 '<listItem listIndent="1" listType="bulleted">Y[]</listItem>' +3532 '<listItem listIndent="1" listType="bulleted">B</listItem>'3533 );3534 } );3535 it( 'should create correct model when list items are pasted in non-list context', () => {3536 setModelData( model,3537 '<paragraph>A[]</paragraph>' +3538 '<paragraph>B</paragraph>'3539 );3540 const clipboard = editor.plugins.get( 'Clipboard' );3541 clipboard.fire( 'inputTransformation', {3542 content: parseView( '<ul><li>X<ul><li>Y</li></ul></li></ul>' )3543 } );3544 expect( getModelData( model ) ).to.equal(3545 '<paragraph>AX</paragraph>' +3546 '<listItem listIndent="0" listType="bulleted">Y[]</listItem>' +3547 '<paragraph>B</paragraph>'3548 );3549 } );3550 it( 'should not crash when "empty content" is inserted', () => {3551 setModelData( model, '<paragraph>[]</paragraph>' );3552 expect( () => {3553 model.change( writer => {3554 editor.model.insertContent( writer.createDocumentFragment() );3555 } );3556 } ).not.to.throw();3557 } );3558 it( 'should correctly handle item that is pasted without its parent', () => {3559 // Wrap all changes in one block to avoid post-fixing the selection3560 // (which may be incorret) in the meantime.3561 model.change( () => {3562 setModelData( model,3563 '<paragraph>Foo</paragraph>' +3564 '<listItem listType="numbered" listIndent="0">A</listItem>' +3565 '<listItem listType="numbered" listIndent="1">B</listItem>' +3566 '[]' +3567 '<paragraph>Bar</paragraph>'3568 );3569 const clipboard = editor.plugins.get( 'Clipboard' );3570 clipboard.fire( 'inputTransformation', {3571 content: parseView( '<li>X</li>' )3572 } );3573 } );3574 expect( getModelData( model ) ).to.equal(3575 '<paragraph>Foo</paragraph>' +3576 '<listItem listIndent="0" listType="numbered">A</listItem>' +3577 '<listItem listIndent="1" listType="numbered">B</listItem>' +3578 '<listItem listIndent="1" listType="numbered">X[]</listItem>' +3579 '<paragraph>Bar</paragraph>'3580 );3581 } );3582 it( 'should correctly handle item that is pasted without its parent #2', () => {3583 // Wrap all changes in one block to avoid post-fixing the selection3584 // (which may be incorret) in the meantime.3585 model.change( () => {3586 setModelData( model,3587 '<paragraph>Foo</paragraph>' +3588 '<listItem listType="numbered" listIndent="0">A</listItem>' +3589 '<listItem listType="numbered" listIndent="1">B</listItem>' +3590 '[]' +3591 '<paragraph>Bar</paragraph>'3592 );3593 const clipboard = editor.plugins.get( 'Clipboard' );3594 clipboard.fire( 'inputTransformation', {3595 content: parseView( '<li>X<ul><li>Y</li></ul></li>' )3596 } );3597 } );3598 expect( getModelData( model ) ).to.equal(3599 '<paragraph>Foo</paragraph>' +3600 '<listItem listIndent="0" listType="numbered">A</listItem>' +3601 '<listItem listIndent="1" listType="numbered">B</listItem>' +3602 '<listItem listIndent="1" listType="numbered">X</listItem>' +3603 '<listItem listIndent="2" listType="bulleted">Y[]</listItem>' +3604 '<paragraph>Bar</paragraph>'3605 );3606 } );3607 it( 'should handle block elements inside pasted list #1', () => {3608 setModelData( model,3609 '<listItem listType="bulleted" listIndent="0">A</listItem>' +3610 '<listItem listType="bulleted" listIndent="1">B[]</listItem>' +3611 '<listItem listType="bulleted" listIndent="2">C</listItem>'3612 );3613 const clipboard = editor.plugins.get( 'Clipboard' );3614 clipboard.fire( 'inputTransformation', {3615 content: parseView( '<ul><li>W<ul><li>X<p>Y</p>Z</li></ul></li></ul>' )3616 } );3617 expect( getModelData( model ) ).to.equal(3618 '<listItem listIndent="0" listType="bulleted">A</listItem>' +3619 '<listItem listIndent="1" listType="bulleted">BW</listItem>' +3620 '<listItem listIndent="2" listType="bulleted">X</listItem>' +3621 '<paragraph>Y</paragraph>' +3622 '<listItem listIndent="0" listType="bulleted">Z[]</listItem>' +3623 '<listItem listIndent="1" listType="bulleted">C</listItem>'3624 );3625 } );3626 it( 'should handle block elements inside pasted list #2', () => {3627 setModelData( model,3628 '<listItem listType="bulleted" listIndent="0">A[]</listItem>' +3629 '<listItem listType="bulleted" listIndent="1">B</listItem>' +3630 '<listItem listType="bulleted" listIndent="2">C</listItem>'3631 );3632 const clipboard = editor.plugins.get( 'Clipboard' );3633 clipboard.fire( 'inputTransformation', {3634 content: parseView( '<ul><li>W<ul><li>X<p>Y</p>Z</li></ul></li></ul>' )3635 } );3636 expect( getModelData( model ) ).to.equal(3637 '<listItem listIndent="0" listType="bulleted">AW</listItem>' +3638 '<listItem listIndent="1" listType="bulleted">X</listItem>' +3639 '<paragraph>Y</paragraph>' +3640 '<listItem listIndent="0" listType="bulleted">Z[]</listItem>' +3641 '<listItem listIndent="0" listType="bulleted">B</listItem>' +3642 '<listItem listIndent="1" listType="bulleted">C</listItem>'3643 );3644 } );3645 it( 'should handle block elements inside pasted list #3', () => {3646 setModelData( model,3647 '<listItem listType="bulleted" listIndent="0">A[]</listItem>' +3648 '<listItem listType="bulleted" listIndent="1">B</listItem>' +3649 '<listItem listType="bulleted" listIndent="2">C</listItem>'3650 );3651 const clipboard = editor.plugins.get( 'Clipboard' );3652 clipboard.fire( 'inputTransformation', {3653 content: parseView( '<ul><li><p>W</p><p>X</p><p>Y</p></li><li>Z</li></ul>' )3654 } );3655 expect( getModelData( model ) ).to.equal(3656 '<listItem listIndent="0" listType="bulleted">AW</listItem>' +3657 '<paragraph>X</paragraph>' +3658 '<paragraph>Y</paragraph>' +3659 '<listItem listIndent="0" listType="bulleted">Z[]</listItem>' +3660 '<listItem listIndent="1" listType="bulleted">B</listItem>' +3661 '<listItem listIndent="2" listType="bulleted">C</listItem>'3662 );3663 } );3664 // https://github.com/ckeditor/ckeditor5-list/issues/126#issuecomment-5182067433665 it( 'should properly handle split of list items with non-standard converters', () => {3666 setModelData( model,3667 '<listItem listType="bulleted" listIndent="0">A[]</listItem>' +3668 '<listItem listType="bulleted" listIndent="1">B</listItem>' +3669 '<listItem listType="bulleted" listIndent="2">C</listItem>'3670 );3671 editor.model.schema.register( 'splitBlock', { inheritAllFrom: '$block' } );3672 editor.conversion.for( 'downcast' ).elementToElement( { model: 'splitBlock', view: 'splitBlock' } );3673 editor.conversion.for( 'upcast' ).add( dispatcher => dispatcher.on( 'element:splitBlock', ( evt, data, conversionApi ) => {3674 conversionApi.consumable.consume( data.viewItem, { name: true } );3675 // Use split to allowed parent logic to simulate a non-standard use of `modelCursor` after split.3676 const splitBlock = conversionApi.writer.createElement( 'splitBlock' );3677 conversionApi.safeInsert( splitBlock, data.modelCursor );3678 data.modelRange = conversionApi.writer.createRangeOn( splitBlock );3679 data.modelCursor = conversionApi.writer.createPositionAfter( splitBlock );3680 } ) );3681 const clipboard = editor.plugins.get( 'Clipboard' );3682 clipboard.fire( 'inputTransformation', {3683 content: parseView( '<ul><li>a<splitBlock></splitBlock>b</li></ul>' )3684 } );3685 expect( getModelData( model, { withoutSelection: true } ) ).to.equal(3686 '<listItem listIndent="0" listType="bulleted">Aa</listItem>' +3687 '<splitBlock></splitBlock>' +3688 '<listItem listIndent="0" listType="bulleted">b</listItem>' +3689 '<listItem listIndent="1" listType="bulleted">B</listItem>' +3690 '<listItem listIndent="2" listType="bulleted">C</listItem>'3691 );3692 } );3693 } );3694 describe( 'other', () => {3695 it( 'model insert converter should not fire if change was already consumed', () => {3696 editor.editing.downcastDispatcher.on( 'insert:listItem', ( evt, data, conversionApi ) => {3697 conversionApi.consumable.consume( data.item, 'attribute:listType' );3698 conversionApi.consumable.consume( data.item, 'attribute:listIndent' );3699 }, { priority: 'highest' } );3700 editor.conversion.for( 'downcast' )3701 .elementToElement( { model: 'listItem', view: 'p', converterPriority: 'highest' } );3702 // Paragraph is needed, otherwise selection throws.3703 setModelData( model, '<paragraph>x</paragraph><listItem listIndent="0" listType="bulleted">y</listItem>' );3704 expect( getViewData( editor.editing.view, { withoutSelection: true } ) ).to.equal( '<p>x</p><p>y</p>' );3705 } );3706 it( 'model remove converter should be possible to overwrite', () => {3707 editor.editing.downcastDispatcher.on( 'remove:listItem', evt => {3708 evt.stop();3709 }, { priority: 'highest' } );3710 // Paragraph is needed to prevent autoparagraphing of empty editor.3711 setModelData( model, '<paragraph>x</paragraph><listItem listIndent="0" listType="bulleted"></listItem>' );3712 model.change( writer => {3713 writer.remove( modelRoot.getChild( 1 ) );3714 } );3715 expect( getViewData( editor.editing.view, { withoutSelection: true } ) ).to.equal( '<p>x</p><ul><li></li></ul>' );3716 } );3717 it( 'model change type converter should not fire if change was already consumed', () => {3718 editor.editing.downcastDispatcher.on( 'attribute:listType', ( evt, data, conversionApi ) => {3719 conversionApi.consumable.consume( data.item, 'attribute:listType' );3720 }, { priority: 'highest' } );3721 setModelData( model, '<listItem listIndent="0" listType="bulleted"></listItem>' );3722 model.change( writer => {3723 writer.setAttribute( 'listType', 'numbered', modelRoot.getChild( 0 ) );3724 } );3725 expect( getViewData( editor.editing.view, { withoutSelection: true } ) ).to.equal( '<ul><li></li></ul>' );3726 } );3727 it( 'model change indent converter should not fire if change was already consumed', () => {3728 editor.editing.downcastDispatcher.on( 'attribute:listIndent', ( evt, data, conversionApi ) => {3729 conversionApi.consumable.consume( data.item, 'attribute:listIndent' );3730 }, { priority: 'highest' } );3731 setModelData(3732 model,3733 '<listItem listIndent="0" listType="bulleted">a</listItem><listItem listIndent="0" listType="bulleted">b</listItem>'3734 );3735 model.change( writer => {3736 writer.setAttribute( 'listIndent', 1, modelRoot.getChild( 1 ) );3737 } );3738 expect( getViewData( editor.editing.view, { withoutSelection: true } ) ).to.equal( '<ul><li>a</li><li>b</li></ul>' );3739 } );3740 it( 'view li converter should not fire if change was already consumed', () => {3741 editor.data.upcastDispatcher.on( 'element:li', ( evt, data, conversionApi ) => {3742 conversionApi.consumable.consume( data.viewItem, { name: true } );3743 }, { priority: 'highest' } );3744 editor.setData( '<p></p><ul><li></li></ul>' );3745 expect( getModelData( model, { withoutSelection: true } ) ).to.equal( '<paragraph></paragraph>' );3746 } );3747 it( 'view ul converter should not fire if change was already consumed', () => {3748 editor.data.upcastDispatcher.on( 'element:ul', ( evt, data, conversionApi ) => {3749 conversionApi.consumable.consume( data.viewItem, { name: true } );3750 }, { priority: 'highest' } );3751 editor.setData( '<p></p><ul><li></li></ul>' );3752 expect( getModelData( model, { withoutSelection: true } ) ).to.equal( '<paragraph></paragraph>' );3753 } );3754 it( 'view converter should pass model range in data.modelRange', () => {3755 editor.data.upcastDispatcher.on( 'element:ul', ( evt, data ) => {3756 expect( data.modelRange ).to.be.instanceof( ModelRange );3757 }, { priority: 'lowest' } );3758 editor.setData( '<ul><li>Foo</li><li>Bar</li></ul>' );3759 } );3760 // This test tests the fix in `injectViewList` helper.3761 it( 'ul and ol should not be inserted before ui element - injectViewList()', () => {3762 editor.setData( '<ul><li>Foo</li><li>Bar</li></ul>' );3763 // Append ui element at the end of first <li>.3764 view.change( writer => {3765 const firstChild = viewDoc.getRoot().getChild( 0 ).getChild( 0 );3766 const uiElement = writer.createUIElement( 'span' );3767 writer.insert( writer.createPositionAt( firstChild, 'end' ), uiElement );3768 } );3769 expect( getViewData( editor.editing.view, { withoutSelection: true } ) )3770 .to.equal( '<ul><li>Foo<span></span></li><li>Bar</li></ul>' );3771 model.change( writer => {3772 // Change indent of the second list item.3773 writer.setAttribute( 'listIndent', 1, modelRoot.getChild( 1 ) );3774 } );3775 // Check if the new <ul> was added at correct position.3776 expect( getViewData( editor.editing.view, { withoutSelection: true } ) )3777 .to.equal( '<ul><li>Foo<span></span><ul><li>Bar</li></ul></li></ul>' );3778 } );3779 // This test tests the fix in `hoistNestedLists` helper.3780 it( 'ul and ol should not be inserted before ui element - hoistNestedLists()', () => {3781 editor.setData( '<ul><li>Foo</li><li>Bar<ul><li>Xxx</li><li>Yyy</li></ul></li></ul>' );3782 // Append ui element at the end of first <li>.3783 view.change( writer => {3784 const firstChild = viewDoc.getRoot().getChild( 0 ).getChild( 0 );3785 const uiElement = writer.createUIElement( 'span' );3786 writer.insert( writer.createPositionAt( firstChild, 'end' ), uiElement );3787 } );3788 expect( getViewData( editor.editing.view, { withoutSelection: true } ) )3789 .to.equal( '<ul><li>Foo<span></span></li><li>Bar<ul><li>Xxx</li><li>Yyy</li></ul></li></ul>' );3790 model.change( writer => {3791 // Remove second list item. Expect that its sub-list will be moved to first list item.3792 writer.remove( modelRoot.getChild( 1 ) );3793 } );3794 // Check if the <ul> was added at correct position.3795 expect( getViewData( editor.editing.view, { withoutSelection: true } ) )3796 .to.equal( '<ul><li>Foo<span></span><ul><li>Xxx</li><li>Yyy</li></ul></li></ul>' );3797 } );3798 describe( 'remove converter should properly handle ui elements', () => {3799 let liFoo, liBar;3800 beforeEach( () => {3801 editor.setData( '<ul><li>Foo</li><li>Bar</li></ul>' );3802 liFoo = modelRoot.getChild( 0 );3803 liBar = modelRoot.getChild( 1 );3804 } );3805 it( 'ui element before <ul>', () => {3806 view.change( writer => {3807 // Append ui element before <ul>.3808 writer.insert( writer.createPositionAt( viewRoot, 0 ), writer.createUIElement( 'span' ) );3809 } );3810 model.change( writer => {3811 writer.remove( liFoo );3812 } );3813 expect( getViewData( editor.editing.view, { withoutSelection: true } ) )3814 .to.equal( '<span></span><ul><li>Bar</li></ul>' );3815 } );3816 it( 'ui element before first <li>', () => {3817 view.change( writer => {3818 // Append ui element before <ul>.3819 writer.insert( writer.createPositionAt( viewRoot.getChild( 0 ), 0 ), writer.createUIElement( 'span' ) );3820 } );3821 model.change( writer => {3822 writer.remove( liFoo );3823 } );3824 expect( getViewData( editor.editing.view, { withoutSelection: true } ) )3825 .to.equal( '<ul><span></span><li>Bar</li></ul>' );3826 } );3827 it( 'ui element in the middle of list', () => {3828 view.change( writer => {3829 // Append ui element before <ul>.3830 writer.insert( writer.createPositionAt( viewRoot.getChild( 0 ), 'end' ), writer.createUIElement( 'span' ) );3831 } );3832 model.change( writer => {3833 writer.remove( liBar );3834 } );3835 expect( getViewData( editor.editing.view, { withoutSelection: true } ) )3836 .to.equal( '<ul><li>Foo</li><span></span></ul>' );3837 } );3838 } );3839 } );3840 describe( 'schema checking and parent splitting', () => {3841 beforeEach( () => {3842 // Since this part of test tests only view->model conversion editing pipeline is not necessary.3843 editor.editing.destroy();3844 } );3845 it( 'list should be not converted when modelCursor and its ancestors disallow to insert list', () => {3846 model.document.createRoot( '$title', 'title' );3847 model.schema.register( '$title', {3848 disallow: '$block',3849 allow: 'inline'3850 } );3851 editor.data.set( { title: '<ul><li>foo</li></ul>' } );3852 expect( getModelData( model, { rootName: 'title', withoutSelection: true } ) ).to.equal( '' );3853 } );3854 it( 'should split parent element when one of modelCursor ancestors allows to insert list - in the middle', () => {3855 editor.conversion.for( 'upcast' ).elementToElement( { view: 'div', model: 'div' } );3856 model.schema.register( 'div', { inheritAllFrom: '$block' } );3857 editor.setData(3858 '<div>' +3859 'abc' +3860 '<ul>' +3861 '<li>foo</li>' +3862 '</ul>' +3863 'def' +3864 '</div>'3865 );3866 expect( getModelData( model, { withoutSelection: true } ) ).to.equal(3867 '<div>abc</div>' +3868 '<listItem listIndent="0" listType="bulleted">foo</listItem>' +3869 '<div>def</div>'3870 );3871 } );3872 it( 'should split parent element when one of modelCursor ancestors allows to insert list - at the end', () => {3873 editor.conversion.for( 'upcast' ).elementToElement( { view: 'div', model: 'div' } );3874 model.schema.register( 'div', { inheritAllFrom: '$block' } );3875 editor.setData(3876 '<div>' +3877 'abc' +3878 '<ul>' +3879 '<li>foo</li>' +3880 '</ul>' +3881 '</div>'3882 );3883 expect( getModelData( model, { withoutSelection: true } ) ).to.equal(3884 '<div>abc</div>' +3885 '<listItem listIndent="0" listType="bulleted">foo</listItem>'3886 );3887 } );3888 it( 'should split parent element when one of modelCursor ancestors allows to insert list - at the beginning', () => {3889 editor.conversion.for( 'upcast' ).elementToElement( { view: 'div', model: 'div' } );3890 model.schema.register( 'div', { inheritAllFrom: '$block' } );3891 editor.setData(3892 '<div>' +3893 '<ul>' +3894 '<li>foo</li>' +3895 '</ul>' +3896 'def' +3897 '</div>'3898 );3899 expect( getModelData( model, { withoutSelection: true } ) ).to.equal(3900 '<listItem listIndent="0" listType="bulleted">foo</listItem>' +3901 '<div>def</div>'3902 );3903 } );3904 // https://github.com/ckeditor/ckeditor5-list/issues/1213905 it( 'should correctly set data.modelCursor', () => {3906 editor.setData(3907 '<ul>' +3908 '<li>a</li>' +3909 '<li>b</li>' +3910 '</ul>' +3911 'c'3912 );3913 expect( getModelData( model, { withoutSelection: true } ) ).to.equal(3914 '<listItem listIndent="0" listType="bulleted">a</listItem>' +3915 '<listItem listIndent="0" listType="bulleted">b</listItem>' +3916 '<paragraph>c</paragraph>'3917 );3918 } );3919 // https://github.com/ckeditor/ckeditor5/issues/15723920 it( 'should not crash if list item contains autoparagraphed block that will be split', () => {3921 // Creating a new editor as we need HeadingEditing. Cannot add HeadingEditing to the `describe` at the beginning of the3922 // test file because other tests assume that headings are not available.3923 return VirtualTestEditor3924 .create( {3925 plugins: [ ListEditing, HeadingEditing ]3926 } )3927 .then( editor => {3928 editor.setData( '<ul><li><div><h2>Foo</h2></div></li></ul>' );3929 expect( getModelData( editor.model, { withoutSelection: true } ) ).to.equal( '<heading1>Foo</heading1>' );3930 } );3931 } );3932 } );3933 function getViewPosition( root, path, view ) {3934 let parent = root;3935 while ( path.length > 1 ) {3936 parent = parent.getChild( path.shift() );3937 }3938 return view.createPositionAt( parent, path[ 0 ] );3939 }3940 function getViewPath( position ) {3941 const path = [ position.offset ];3942 let parent = position.parent;3943 while ( parent.parent ) {3944 path.unshift( parent.index );3945 parent = parent.parent;3946 }3947 return path;3948 }3949 function testInsert( testName, input, output, testUndo = true ) {3950 // Cut out inserted element that is between '[' and ']' characters.3951 const selStart = input.indexOf( '[' ) + 1;3952 const selEnd = input.indexOf( ']' );3953 const item = input.substring( selStart, selEnd );3954 const modelInput = input.substring( 0, selStart ) + input.substring( selEnd );3955 const actionCallback = selection => {3956 model.change( writer => {3957 writer.insert( parseModel( item, model.schema ), selection.getFirstPosition() );3958 } );3959 };3960 _test( testName, modelInput, output, actionCallback, testUndo );3961 }3962 function testRemove( testName, input, output ) {3963 const actionCallback = selection => {3964 model.change( writer => {3965 writer.remove( selection.getFirstRange() );3966 } );3967 };3968 _test( testName, input, output, actionCallback );3969 }3970 function testChangeType( testName, input, output ) {3971 const actionCallback = selection => {3972 const element = selection.getFirstPosition().nodeAfter;3973 const newType = element.getAttribute( 'listType' ) == 'numbered' ? 'bulleted' : 'numbered';3974 model.change( writer => {3975 const itemsToChange = Array.from( selection.getSelectedBlocks() );3976 for ( const item of itemsToChange ) {3977 writer.setAttribute( 'listType', newType, item );3978 }3979 } );3980 };3981 _test( testName, input, output, actionCallback );3982 }3983 function testRenameFromListItem( testName, input, output, testUndo = true ) {3984 const actionCallback = selection => {3985 const element = selection.getFirstPosition().nodeAfter;3986 model.change( writer => {3987 writer.rename( element, 'paragraph' );3988 writer.removeAttribute( 'listType', element );3989 writer.removeAttribute( 'listIndent', element );3990 } );3991 };3992 _test( testName, input, output, actionCallback, testUndo );3993 }3994 function testRenameToListItem( testName, newIndent, input, output ) {3995 const actionCallback = selection => {3996 const element = selection.getFirstPosition().nodeAfter;3997 model.change( writer => {3998 writer.setAttributes( { listType: 'bulleted', listIndent: newIndent }, element );3999 writer.rename( element, 'listItem' );4000 } );4001 };4002 _test( testName, input, output, actionCallback );4003 }4004 function testChangeIndent( testName, newIndent, input, output ) {4005 const actionCallback = selection => {4006 model.change( writer => {4007 writer.setAttribute( 'listIndent', newIndent, selection.getFirstRange() );4008 } );4009 };4010 _test( testName, input, output, actionCallback );4011 }4012 function testMove( testName, input, rootOffset, output, testUndo = true ) {4013 const actionCallback = selection => {4014 model.change( writer => {4015 const targetPosition = writer.createPositionAt( modelRoot, rootOffset );4016 writer.move( selection.getFirstRange(), targetPosition );4017 } );4018 };4019 _test( testName, input, output, actionCallback, testUndo );4020 }4021 function _test( testName, input, output, actionCallback, testUndo ) {4022 it( testName, () => {4023 const callbackSelection = prepareTest( model, input );4024 actionCallback( callbackSelection );4025 expect( getViewData( view, { withoutSelection: true } ) ).to.equal( output );4026 } );4027 if ( testUndo ) {4028 it( testName + ' (undo integration)', () => {4029 const callbackSelection = prepareTest( model, input );4030 const modelBefore = getModelData( model );4031 const viewBefore = getViewData( view, { withoutSelection: true } );4032 actionCallback( callbackSelection );4033 const modelAfter = getModelData( model );4034 const viewAfter = getViewData( view, { withoutSelection: true } );4035 editor.execute( 'undo' );4036 expect( getModelData( model ) ).to.equal( modelBefore );4037 expect( getViewData( view, { withoutSelection: true } ) ).to.equal( viewBefore );4038 editor.execute( 'redo' );4039 expect( getModelData( model ) ).to.equal( modelAfter );4040 expect( getViewData( view, { withoutSelection: true } ) ).to.equal( viewAfter );4041 } );4042 }4043 function prepareTest( model, input ) {4044 const modelRoot = model.document.getRoot( 'main' );4045 // Parse data string to model.4046 const parsedResult = parseModel( input, model.schema, { context: [ modelRoot.name ] } );4047 // Retrieve DocumentFragment and Selection from parsed model.4048 const modelDocumentFragment = parsedResult.model;4049 const selection = parsedResult.selection;4050 // Ensure no undo step is generated.4051 model.enqueueChange( 'transparent', writer => {4052 // Replace existing model in document by new one.4053 writer.remove( writer.createRangeIn( modelRoot ) );4054 writer.insert( modelDocumentFragment, modelRoot );4055 // Clean up previous document selection.4056 writer.setSelection( null );4057 writer.removeSelectionAttribute( model.document.selection.getAttributeKeys() );4058 } );4059 const ranges = [];4060 for ( const range of selection.getRanges() ) {4061 const start = model.createPositionFromPath( modelRoot, range.start.path );4062 const end = model.createPositionFromPath( modelRoot, range.end.path );4063 ranges.push( model.createRange( start, end ) );4064 }4065 return model.createSelection( ranges );4066 }4067 }...
liststyleediting.js
Source:liststyleediting.js
1/**2 * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.3 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license4 */5import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';6import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';7import Typing from '@ckeditor/ckeditor5-typing/src/typing';8import UndoEditing from '@ckeditor/ckeditor5-undo/src/undoediting';9import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';10import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';11import ListStyleEditing from '../src/liststyleediting';12import TodoListEditing from '../src/todolistediting';13import ListStyleCommand from '../src/liststylecommand';14describe( 'ListStyleEditing', () => {15 let editor, model, view;16 beforeEach( () => {17 return VirtualTestEditor18 .create( {19 plugins: [ Paragraph, ListStyleEditing, UndoEditing ]20 } )21 .then( newEditor => {22 editor = newEditor;23 model = editor.model;24 view = editor.editing.view;25 } );26 } );27 afterEach( () => {28 return editor.destroy();29 } );30 it( 'should have pluginName', () => {31 expect( ListStyleEditing.pluginName ).to.equal( 'ListStyleEditing' );32 } );33 it( 'should be loaded', () => {34 expect( editor.plugins.get( ListStyleEditing ) ).to.be.instanceOf( ListStyleEditing );35 } );36 describe( 'schema rules', () => {37 it( 'should allow set `listStyle` on the `listItem`', () => {38 expect( model.schema.checkAttribute( [ '$root', 'listItem' ], 'listStyle' ) ).to.be.true;39 } );40 } );41 describe( 'command', () => {42 it( 'should register listStyle command', () => {43 const command = editor.commands.get( 'listStyle' );44 expect( command ).to.be.instanceOf( ListStyleCommand );45 } );46 } );47 describe( 'conversion in data pipeline', () => {48 describe( 'model to data', () => {49 it( 'should convert single list (type: bulleted)', () => {50 setModelData( model,51 '<listItem listIndent="0" listType="bulleted">Foo</listItem>' +52 '<listItem listIndent="0" listType="bulleted">Bar</listItem>'53 );54 expect( editor.getData() ).to.equal( '<ul><li>Foo</li><li>Bar</li></ul>' );55 } );56 it( 'should convert single list (type: numbered)', () => {57 setModelData( model,58 '<listItem listIndent="0" listType="numbered">Foo</listItem>' +59 '<listItem listIndent="0" listType="numbered">Bar</listItem>'60 );61 expect( editor.getData() ).to.equal( '<ol><li>Foo</li><li>Bar</li></ol>' );62 } );63 it( 'should convert single list (type: bulleted, style: default)', () => {64 setModelData( model,65 '<listItem listIndent="0" listType="bulleted" listStyle="default">Foo</listItem>' +66 '<listItem listIndent="0" listType="bulleted" listStyle="default">Bar</listItem>'67 );68 expect( editor.getData() ).to.equal( '<ul><li>Foo</li><li>Bar</li></ul>' );69 } );70 it( 'should convert single list (type: numbered, style: default)', () => {71 setModelData( model,72 '<listItem listIndent="0" listType="numbered" listStyle="default">Foo</listItem>' +73 '<listItem listIndent="0" listType="numbered" listStyle="default">Bar</listItem>'74 );75 expect( editor.getData() ).to.equal( '<ol><li>Foo</li><li>Bar</li></ol>' );76 } );77 it( 'should convert single list (type: bulleted, style: circle)', () => {78 setModelData( model,79 '<listItem listIndent="0" listType="bulleted" listStyle="circle">Foo</listItem>' +80 '<listItem listIndent="0" listType="bulleted" listStyle="circle">Bar</listItem>'81 );82 expect( editor.getData() ).to.equal( '<ul style="list-style-type:circle;"><li>Foo</li><li>Bar</li></ul>' );83 } );84 it( 'should convert single list (type: numbered, style: upper-alpha)', () => {85 setModelData( model,86 '<listItem listIndent="0" listType="numbered" listStyle="upper-alpha">Foo</listItem>' +87 '<listItem listIndent="0" listType="numbered" listStyle="upper-alpha">Bar</listItem>'88 );89 expect( editor.getData() ).to.equal( '<ol style="list-style-type:upper-alpha;"><li>Foo</li><li>Bar</li></ol>' );90 } );91 it( 'should convert nested bulleted lists (main: circle, nested: disc)', () => {92 setModelData( model,93 '<listItem listIndent="0" listType="bulleted" listStyle="circle">Foo 1</listItem>' +94 '<listItem listIndent="1" listType="bulleted" listStyle="disc">Bar 1</listItem>' +95 '<listItem listIndent="1" listType="bulleted" listStyle="disc">Bar 2</listItem>' +96 '<listItem listIndent="0" listType="bulleted" listStyle="circle">Foo 2</listItem>' +97 '<listItem listIndent="0" listType="bulleted" listStyle="circle">Foo 3</listItem>'98 );99 expect( editor.getData() ).to.equal(100 '<ul style="list-style-type:circle;">' +101 '<li>Foo 1' +102 '<ul style="list-style-type:disc;">' +103 '<li>Bar 1</li>' +104 '<li>Bar 2</li>' +105 '</ul>' +106 '</li>' +107 '<li>Foo 2</li>' +108 '<li>Foo 3</li>' +109 '</ul>'110 );111 } );112 it( 'should convert nested numbered lists (main: decimal-leading-zero, nested: lower-latin)', () => {113 setModelData( model,114 '<listItem listIndent="0" listType="numbered" listStyle="decimal-leading-zero">Foo 1</listItem>' +115 '<listItem listIndent="1" listType="numbered" listStyle="lower-latin">Bar 1</listItem>' +116 '<listItem listIndent="1" listType="numbered" listStyle="lower-latin">Bar 2</listItem>' +117 '<listItem listIndent="0" listType="numbered" listStyle="decimal-leading-zero">Foo 2</listItem>' +118 '<listItem listIndent="0" listType="numbered" listStyle="decimal-leading-zero">Foo 3</listItem>'119 );120 expect( editor.getData() ).to.equal(121 '<ol style="list-style-type:decimal-leading-zero;">' +122 '<li>Foo 1' +123 '<ol style="list-style-type:lower-latin;">' +124 '<li>Bar 1</li>' +125 '<li>Bar 2</li>' +126 '</ol>' +127 '</li>' +128 '<li>Foo 2</li>' +129 '<li>Foo 3</li>' +130 '</ol>'131 );132 } );133 it( 'should convert nested mixed lists (ul>ol, main: square, nested: lower-roman)', () => {134 setModelData( model,135 '<listItem listIndent="0" listType="bulleted" listStyle="square">Foo 1</listItem>' +136 '<listItem listIndent="1" listType="numbered" listStyle="lower-roman">Bar 1</listItem>' +137 '<listItem listIndent="1" listType="numbered" listStyle="lower-roman">Bar 2</listItem>' +138 '<listItem listIndent="0" listType="bulleted" listStyle="square">Foo 2</listItem>' +139 '<listItem listIndent="0" listType="bulleted" listStyle="square">Foo 3</listItem>'140 );141 expect( editor.getData() ).to.equal(142 '<ul style="list-style-type:square;">' +143 '<li>Foo 1' +144 '<ol style="list-style-type:lower-roman;">' +145 '<li>Bar 1</li>' +146 '<li>Bar 2</li>' +147 '</ol>' +148 '</li>' +149 '<li>Foo 2</li>' +150 '<li>Foo 3</li>' +151 '</ul>'152 );153 } );154 it( 'should produce nested lists (different `listIndent` attribute)', () => {155 setModelData( model,156 '<listItem listIndent="0" listType="numbered" listStyle="decimal">Foo 1</listItem>' +157 '<listItem listIndent="0" listType="numbered" listStyle="decimal">Foo 2</listItem>' +158 '<listItem listIndent="1" listType="numbered" listStyle="decimal">Bar 1</listItem>' +159 '<listItem listIndent="1" listType="numbered" listStyle="decimal">Bar 2</listItem>'160 );161 expect( editor.getData() ).to.equal(162 '<ol style="list-style-type:decimal;">' +163 '<li>Foo 1</li>' +164 '<li>Foo 2' +165 '<ol style="list-style-type:decimal;">' +166 '<li>Bar 1</li>' +167 '<li>Bar 2</li>' +168 '</ol>' +169 '</li>' +170 '</ol>'171 );172 } );173 it( 'should produce two different lists (different `listType` attribute)', () => {174 setModelData( model,175 '<listItem listIndent="0" listType="numbered" listStyle="decimal">Foo 1</listItem>' +176 '<listItem listIndent="0" listType="numbered" listStyle="decimal">Foo 2</listItem>' +177 '<listItem listIndent="0" listType="bulleted" listStyle="disc">Bar 1</listItem>' +178 '<listItem listIndent="0" listType="bulleted" listStyle="disc">Bar 2</listItem>'179 );180 expect( editor.getData() ).to.equal(181 '<ol style="list-style-type:decimal;">' +182 '<li>Foo 1</li>' +183 '<li>Foo 2</li>' +184 '</ol>' +185 '<ul style="list-style-type:disc;">' +186 '<li>Bar 1</li>' +187 '<li>Bar 2</li>' +188 '</ul>'189 );190 } );191 it( 'should produce two different lists (different `listStyle` attribute)', () => {192 setModelData( model,193 '<listItem listIndent="0" listType="bulleted" listStyle="disc">Foo 1</listItem>' +194 '<listItem listIndent="0" listType="bulleted" listStyle="disc">Foo 2</listItem>' +195 '<listItem listIndent="0" listType="bulleted" listStyle="circle">Bar 1</listItem>' +196 '<listItem listIndent="0" listType="bulleted" listStyle="circle">Bar 2</listItem>'197 );198 expect( editor.getData() ).to.equal(199 '<ul style="list-style-type:disc;">' +200 '<li>Foo 1</li>' +201 '<li>Foo 2</li>' +202 '</ul>' +203 '<ul style="list-style-type:circle;">' +204 '<li>Bar 1</li>' +205 '<li>Bar 2</li>' +206 '</ul>'207 );208 } );209 it( 'should not allow to set the `listStyle` attribute in to-do list item', () => {210 setModelData( model, '<listItem listIndent="0" listType="todo">Foo</listItem>' );211 const listItem = model.document.getRoot().getChild( 0 );212 expect( listItem.hasAttribute( 'listItem' ) ).to.be.false;213 model.change( writer => {214 writer.setAttribute( 'listType', 'foo', listItem );215 } );216 expect( listItem.hasAttribute( 'listItem' ) ).to.be.false;217 } );218 } );219 describe( 'view to model', () => {220 it( 'should convert single list (type: bulleted)', () => {221 editor.setData( '<ul><li>Foo</li><li>Bar</li></ul>' );222 expect( getModelData( model, { withoutSelection: true } ) ).to.equal(223 '<listItem listIndent="0" listStyle="default" listType="bulleted">Foo</listItem>' +224 '<listItem listIndent="0" listStyle="default" listType="bulleted">Bar</listItem>'225 );226 } );227 it( 'should convert single list (type: numbered)', () => {228 editor.setData( '<ol><li>Foo</li><li>Bar</li></ol>' );229 expect( getModelData( model, { withoutSelection: true } ) ).to.equal(230 '<listItem listIndent="0" listStyle="default" listType="numbered">Foo</listItem>' +231 '<listItem listIndent="0" listStyle="default" listType="numbered">Bar</listItem>'232 );233 } );234 it( 'should convert single list (type: bulleted, style: circle)', () => {235 editor.setData( '<ul style="list-style-type:circle;"><li>Foo</li><li>Bar</li></ul>' );236 expect( getModelData( model, { withoutSelection: true } ) ).to.equal(237 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +238 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'239 );240 } );241 it( 'should convert single list (type: numbered, style: upper-alpha)', () => {242 editor.setData( '<ol style="list-style-type:upper-alpha;"><li>Foo</li><li>Bar</li></ol>' );243 expect( getModelData( model, { withoutSelection: true } ) ).to.equal(244 '<listItem listIndent="0" listStyle="upper-alpha" listType="numbered">Foo</listItem>' +245 '<listItem listIndent="0" listStyle="upper-alpha" listType="numbered">Bar</listItem>'246 );247 } );248 it( 'should convert nested and mixed lists', () => {249 editor.setData(250 '<ol style="list-style-type:upper-alpha;">' +251 '<li>OL 1</li>' +252 '<li>OL 2' +253 '<ul style="list-style-type:circle;">' +254 '<li>UL 1</li>' +255 '<li>UL 2</li>' +256 '</ul>' +257 '</li>' +258 '<li>OL 3</li>' +259 '</ol>'260 );261 expect( getModelData( model, { withoutSelection: true } ) ).to.equal(262 '<listItem listIndent="0" listStyle="upper-alpha" listType="numbered">OL 1</listItem>' +263 '<listItem listIndent="0" listStyle="upper-alpha" listType="numbered">OL 2</listItem>' +264 '<listItem listIndent="1" listStyle="circle" listType="bulleted">UL 1</listItem>' +265 '<listItem listIndent="1" listStyle="circle" listType="bulleted">UL 2</listItem>' +266 '<listItem listIndent="0" listStyle="upper-alpha" listType="numbered">OL 3</listItem>'267 );268 } );269 it( 'should convert when the list is in the middle of the content', () => {270 editor.setData(271 '<p>Paragraph.</p>' +272 '<ol style="list-style-type:upper-alpha;">' +273 '<li>Foo</li>' +274 '<li>Bar</li>' +275 '</ol>' +276 '<p>Paragraph.</p>'277 );278 expect( getModelData( model, { withoutSelection: true } ) ).to.equal(279 '<paragraph>Paragraph.</paragraph>' +280 '<listItem listIndent="0" listStyle="upper-alpha" listType="numbered">Foo</listItem>' +281 '<listItem listIndent="0" listStyle="upper-alpha" listType="numbered">Bar</listItem>' +282 '<paragraph>Paragraph.</paragraph>'283 );284 } );285 // See: #8262.286 describe( 'list conversion with surrounding text nodes', () => {287 let editor;288 beforeEach( () => {289 return VirtualTestEditor290 .create( {291 plugins: [ ListStyleEditing ]292 } )293 .then( newEditor => {294 editor = newEditor;295 } );296 } );297 afterEach( () => {298 return editor.destroy();299 } );300 it( 'should convert a list if raw text is before the list', () => {301 editor.setData( 'Foo<ul><li>Foo</li></ul>' );302 expect( editor.getData() ).to.equal( '<p>Foo</p><ul><li>Foo</li></ul>' );303 } );304 it( 'should convert a list if raw text is after the list', () => {305 editor.setData( '<ul><li>Foo</li></ul>Foo' );306 expect( editor.getData() ).to.equal( '<ul><li>Foo</li></ul><p>Foo</p>' );307 } );308 it( 'should convert a list if it is surrender by two text nodes', () => {309 editor.setData( 'Foo<ul><li>Foo</li></ul>Foo' );310 expect( editor.getData() ).to.equal( '<p>Foo</p><ul><li>Foo</li></ul><p>Foo</p>' );311 } );312 } );313 } );314 } );315 // At this moment editing and data pipelines produce exactly the same content.316 // Just a few tests will be enough here. `model to data` block contains all cases checked.317 describe( 'conversion in editing pipeline', () => {318 describe( 'model to view', () => {319 it( 'should convert single list (type: bulleted, style: default)', () => {320 setModelData( model,321 '<listItem listIndent="0" listType="bulleted" listStyle="default">Foo</listItem>' +322 '<listItem listIndent="0" listType="bulleted" listStyle="default">Bar</listItem>'323 );324 expect( getViewData( view, { withoutSelection: true } ) ).to.equal(325 '<ul><li>Foo</li><li>Bar</li></ul>'326 );327 } );328 it( 'should convert single list (type: bulleted, style: circle)', () => {329 setModelData( model,330 '<listItem listIndent="0" listType="bulleted" listStyle="circle">Foo</listItem>' +331 '<listItem listIndent="0" listType="bulleted" listStyle="circle">Bar</listItem>'332 );333 expect( getViewData( view, { withoutSelection: true } ) ).to.equal(334 '<ul style="list-style-type:circle"><li>Foo</li><li>Bar</li></ul>'335 );336 } );337 it( 'should convert nested bulleted lists (main: circle, nested: disc)', () => {338 setModelData( model,339 '<listItem listIndent="0" listType="bulleted" listStyle="circle">Foo 1</listItem>' +340 '<listItem listIndent="1" listType="bulleted" listStyle="disc">Bar 1</listItem>' +341 '<listItem listIndent="1" listType="bulleted" listStyle="disc">Bar 2</listItem>' +342 '<listItem listIndent="0" listType="bulleted" listStyle="circle">Foo 2</listItem>' +343 '<listItem listIndent="0" listType="bulleted" listStyle="circle">Foo 3</listItem>'344 );345 expect( getViewData( view, { withoutSelection: true } ) ).to.equal(346 '<ul style="list-style-type:circle">' +347 '<li>Foo 1' +348 '<ul style="list-style-type:disc">' +349 '<li>Bar 1</li>' +350 '<li>Bar 2</li>' +351 '</ul>' +352 '</li>' +353 '<li>Foo 2</li>' +354 '<li>Foo 3</li>' +355 '</ul>'356 );357 } );358 // See: #8081.359 it( 'should convert properly nested list styles', () => {360 // â Level 0361 // â¶ Level 0.1362 // â Level 0.1.1363 // â¶ Level 0.2364 // â Level 0.2.1365 setModelData( model,366 '<listItem listIndent="0" listType="bulleted" listStyle="default">Level 0</listItem>' +367 '<listItem listIndent="1" listType="bulleted" listStyle="default">Level 0.1</listItem>' +368 '<listItem listIndent="2" listType="bulleted" listStyle="circle">Level 0.1.1</listItem>' +369 '<listItem listIndent="1" listType="bulleted" listStyle="default">Level 0.2</listItem>' +370 '<listItem listIndent="2" listType="bulleted" listStyle="circle">Level 0.2.1</listItem>'371 );372 expect( getViewData( view, { withoutSelection: true } ) ).to.equal(373 '<ul>' +374 '<li>Level 0' +375 '<ul>' +376 '<li>Level 0.1' +377 '<ul style="list-style-type:circle">' +378 '<li>Level 0.1.1</li>' +379 '</ul>' +380 '</li>' +381 '<li>Level 0.2' +382 '<ul style="list-style-type:circle">' +383 '<li>Level 0.2.1</li>' +384 '</ul>' +385 '</li>' +386 '</ul>' +387 '</li>' +388 '</ul>'389 );390 } );391 } );392 } );393 describe( 'integrations', () => {394 describe( 'merging a list into a styled list', () => {395 it( 'should inherit the list style attribute when merging the same kind of lists (from top, merge a single item)', () => {396 setModelData( model,397 '<paragraph>Foo Bar.[]</paragraph>' +398 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +399 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'400 );401 editor.execute( 'bulletedList' );402 expect( getModelData( model ) ).to.equal(403 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo Bar.[]</listItem>' +404 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +405 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'406 );407 } );408 it( 'should inherit the list style attribute when merging the same kind of lists (from top, merge a few items)', () => {409 setModelData( model,410 '<paragraph>[Foo Bar 1.</paragraph>' +411 '<paragraph>Foo Bar 2.]</paragraph>' +412 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +413 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'414 );415 editor.execute( 'bulletedList' );416 expect( getModelData( model ) ).to.equal(417 '<listItem listIndent="0" listStyle="circle" listType="bulleted">[Foo Bar 1.</listItem>' +418 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo Bar 2.]</listItem>' +419 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +420 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'421 );422 } );423 it( 'should not inherit anything if there is no list below the inserted list', () => {424 setModelData( model,425 '<paragraph>Foo Bar 1.[]</paragraph>' +426 '<paragraph>Foo Bar 2.</paragraph>'427 );428 editor.execute( 'bulletedList' );429 expect( getModelData( model ) ).to.equal(430 '<listItem listIndent="0" listStyle="default" listType="bulleted">Foo Bar 1.[]</listItem>' +431 '<paragraph>Foo Bar 2.</paragraph>'432 );433 } );434 it( 'should not inherit anything if replacing the entire content with a list', () => {435 setModelData( model,436 '<paragraph>Foo Bar 1.[]</paragraph>'437 );438 editor.execute( 'bulletedList' );439 expect( getModelData( model ) ).to.equal(440 '<listItem listIndent="0" listStyle="default" listType="bulleted">Foo Bar 1.[]</listItem>'441 );442 } );443 it( 'should not inherit the list style attribute when merging different kind of lists (from top, merge a single item)', () => {444 setModelData( model,445 '<paragraph>Foo Bar.[]</paragraph>' +446 '<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Foo</listItem>' +447 '<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Bar</listItem>'448 );449 editor.execute( 'bulletedList' );450 expect( getModelData( model ) ).to.equal(451 '<listItem listIndent="0" listStyle="default" listType="bulleted">Foo Bar.[]</listItem>' +452 '<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Foo</listItem>' +453 '<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Bar</listItem>'454 );455 } );456 it(457 'should not inherit the list style attribute when merging different kind of lists (from bottom, merge a single item)',458 () => {459 setModelData( model,460 '<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Foo</listItem>' +461 '<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Bar</listItem>' +462 '<paragraph>Foo Bar.[]</paragraph>'463 );464 editor.execute( 'bulletedList' );465 expect( getModelData( model ) ).to.equal(466 '<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Foo</listItem>' +467 '<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Bar</listItem>' +468 '<listItem listIndent="0" listStyle="default" listType="bulleted">Foo Bar.[]</listItem>'469 );470 }471 );472 it( 'should inherit the list style attribute when merging the same kind of lists (from bottom, merge a single item)', () => {473 setModelData( model,474 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +475 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>' +476 '<paragraph>Foo Bar.[]</paragraph>'477 );478 editor.execute( 'bulletedList' );479 expect( getModelData( model ) ).to.equal(480 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +481 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>' +482 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo Bar.[]</listItem>'483 );484 } );485 it(486 'should inherit the list style attribute from listIndent=0 element when merging the same kind of lists (from bottom)',487 () => {488 setModelData( model,489 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +490 '<listItem listIndent="1" listStyle="square" listType="bulleted">Bar</listItem>' +491 '<listItem listIndent="2" listStyle="disc" listType="bulleted">Foo Bar</listItem>' +492 '<paragraph>Foo Bar.[]</paragraph>'493 );494 editor.execute( 'bulletedList' );495 expect( getModelData( model ) ).to.equal(496 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +497 '<listItem listIndent="1" listStyle="square" listType="bulleted">Bar</listItem>' +498 '<listItem listIndent="2" listStyle="disc" listType="bulleted">Foo Bar</listItem>' +499 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo Bar.[]</listItem>'500 );501 }502 );503 } );504 describe( 'modifying "listType" attribute', () => {505 it( 'should inherit the list style attribute when the modified list is the same kind of the list as next sibling', () => {506 setModelData( model,507 '<listItem listIndent="0" listStyle="default" listType="numbered">Foo Bar.[]</listItem>' +508 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +509 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'510 );511 editor.execute( 'bulletedList' );512 expect( getModelData( model ) ).to.equal(513 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo Bar.[]</listItem>' +514 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +515 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'516 );517 } );518 it( 'should inherit the list style attribute when the modified list is the same kind of the list as previous sibling', () => {519 setModelData( model,520 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +521 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>' +522 '<listItem listIndent="0" listStyle="default" listType="numbered">Foo Bar.[]</listItem>'523 );524 editor.execute( 'bulletedList' );525 expect( getModelData( model ) ).to.equal(526 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +527 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>' +528 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo Bar.[]</listItem>'529 );530 } );531 it( 'should not inherit the list style attribute when the modified list already has defined it (next sibling check)', () => {532 setModelData( model,533 '<listItem listIndent="0" listStyle="default" listType="bulleted">Foo Bar.[]</listItem>' +534 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +535 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'536 );537 editor.execute( 'listStyle', { type: 'disc' } );538 expect( getModelData( model ) ).to.equal(539 '<listItem listIndent="0" listStyle="disc" listType="bulleted">Foo Bar.[]</listItem>' +540 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +541 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'542 );543 } );544 it(545 'should not inherit the list style attribute when the modified list already has defined it (previous sibling check)',546 () => {547 setModelData( model,548 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +549 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>' +550 '<listItem listIndent="0" listStyle="default" listType="bulleted">Foo Bar.[]</listItem>'551 );552 editor.execute( 'listStyle', { type: 'disc' } );553 expect( getModelData( model ) ).to.equal(554 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +555 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>' +556 '<listItem listIndent="0" listStyle="disc" listType="bulleted">Foo Bar.[]</listItem>'557 );558 }559 );560 } );561 describe( 'indenting lists', () => {562 it( 'should restore the default value for the list style attribute when indenting a single item', () => {563 setModelData( model,564 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +565 '<listItem listIndent="1" listStyle="default" listType="bulleted">1A.</listItem>' +566 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2B.</listItem>' +567 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.[]</listItem>' +568 '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>'569 );570 editor.execute( 'indentList' );571 expect( getModelData( model ) ).to.equal(572 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +573 '<listItem listIndent="1" listStyle="default" listType="bulleted">1A.</listItem>' +574 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2B.</listItem>' +575 '<listItem listIndent="1" listStyle="default" listType="bulleted">2.[]</listItem>' +576 '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>'577 );578 } );579 it( 'should restore the default value for the list style attribute when indenting a few items', () => {580 setModelData( model,581 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +582 '<listItem listIndent="0" listStyle="circle" listType="bulleted">[2.</listItem>' +583 '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.]</listItem>'584 );585 editor.execute( 'indentList' );586 expect( getModelData( model ) ).to.equal(587 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +588 '<listItem listIndent="1" listStyle="default" listType="bulleted">[2.</listItem>' +589 '<listItem listIndent="1" listStyle="default" listType="bulleted">3.]</listItem>'590 );591 } );592 it(593 'should copy the value for the list style attribute when indenting a single item into a nested list (default value)',594 () => {595 setModelData( model,596 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +597 '<listItem listIndent="1" listStyle="default" listType="bulleted">2.</listItem>' +598 '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.[]</listItem>' +599 '<listItem listIndent="0" listStyle="circle" listType="bulleted">4.</listItem>'600 );601 editor.execute( 'indentList' );602 expect( getModelData( model ) ).to.equal(603 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +604 '<listItem listIndent="1" listStyle="default" listType="bulleted">2.</listItem>' +605 '<listItem listIndent="1" listStyle="default" listType="bulleted">3.[]</listItem>' +606 '<listItem listIndent="0" listStyle="circle" listType="bulleted">4.</listItem>'607 );608 }609 );610 it(611 'should copy the value for the list style attribute when indenting a single item into a nested list (changed value)',612 () => {613 setModelData( model,614 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +615 '<listItem listIndent="1" listStyle="disc" listType="bulleted">2.</listItem>' +616 '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.[]</listItem>' +617 '<listItem listIndent="0" listStyle="circle" listType="bulleted">4.</listItem>'618 );619 editor.execute( 'indentList' );620 expect( getModelData( model ) ).to.equal(621 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +622 '<listItem listIndent="1" listStyle="disc" listType="bulleted">2.</listItem>' +623 '<listItem listIndent="1" listStyle="disc" listType="bulleted">3.[]</listItem>' +624 '<listItem listIndent="0" listStyle="circle" listType="bulleted">4.</listItem>'625 );626 }627 );628 it( 'should copy the value for the list style attribute when indenting a single item into a nested list', () => {629 setModelData( model,630 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +631 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.[]</listItem>' +632 '<listItem listIndent="1" listStyle="square" listType="bulleted">3.</listItem>' +633 '<listItem listIndent="0" listStyle="circle" listType="bulleted">4.</listItem>'634 );635 editor.execute( 'indentList' );636 expect( getModelData( model ) ).to.equal(637 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +638 '<listItem listIndent="1" listStyle="default" listType="bulleted">2.[]</listItem>' +639 '<listItem listIndent="2" listStyle="square" listType="bulleted">3.</listItem>' +640 '<listItem listIndent="0" listStyle="circle" listType="bulleted">4.</listItem>'641 );642 } );643 it(644 'should copy the value for the list style attribute when indenting a single item into a nested list ' +645 '(many nested lists check)',646 () => {647 setModelData( model,648 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +649 '<listItem listIndent="1" listStyle="disc" listType="bulleted">2.</listItem>' +650 '<listItem listIndent="2" listStyle="square" listType="bulleted">3.</listItem>' +651 '<listItem listIndent="0" listStyle="circle" listType="bulleted">4.[]</listItem>'652 );653 editor.execute( 'indentList' );654 expect( getModelData( model ) ).to.equal(655 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +656 '<listItem listIndent="1" listStyle="disc" listType="bulleted">2.</listItem>' +657 '<listItem listIndent="2" listStyle="square" listType="bulleted">3.</listItem>' +658 '<listItem listIndent="1" listStyle="disc" listType="bulleted">4.[]</listItem>'659 );660 }661 );662 it( 'should inherit the list style attribute from nested list if the `listType` is other than indenting element', () => {663 setModelData( model,664 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +665 '<listItem listIndent="1" listStyle="decimal" listType="numbered">2.</listItem>' +666 '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.[]</listItem>'667 );668 editor.execute( 'indentList' );669 expect( getModelData( model ) ).to.equal(670 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +671 '<listItem listIndent="1" listStyle="decimal" listType="numbered">2.</listItem>' +672 '<listItem listIndent="1" listStyle="decimal" listType="numbered">3.[]</listItem>'673 );674 } );675 // See: #8072.676 it( 'should not throw when indenting a list without any other content in the editor', () => {677 setModelData( model,678 '<listItem listIndent="0" listStyle="default" listType="bulleted">Foo</listItem>' +679 '<listItem listIndent="0" listStyle="default" listType="bulleted">[]</listItem>'680 );681 editor.execute( 'indentList' );682 expect( getModelData( model ) ).to.equal(683 '<listItem listIndent="0" listStyle="default" listType="bulleted">Foo</listItem>' +684 '<listItem listIndent="1" listStyle="default" listType="bulleted">[]</listItem>'685 );686 } );687 } );688 describe( 'outdenting lists', () => {689 it( 'should inherit the list style attribute from parent list (change the first nested item)', () => {690 setModelData( model,691 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +692 '<listItem listIndent="1" listStyle="default" listType="bulleted">2.[]</listItem>' +693 '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>'694 );695 editor.execute( 'outdentList' );696 expect( getModelData( model ) ).to.equal(697 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +698 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.[]</listItem>' +699 '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>'700 );701 } );702 it( 'should inherit the list style attribute from parent list (change the second nested item)', () => {703 setModelData( model,704 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +705 '<listItem listIndent="1" listStyle="default" listType="bulleted">2.</listItem>' +706 '<listItem listIndent="1" listStyle="default" listType="bulleted">3.[]</listItem>'707 );708 editor.execute( 'outdentList' );709 expect( getModelData( model ) ).to.equal(710 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +711 '<listItem listIndent="1" listStyle="default" listType="bulleted">2.</listItem>' +712 '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.[]</listItem>'713 );714 } );715 it( 'should inherit the list style attribute from parent list (modifying nested lists)', () => {716 setModelData( model,717 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +718 '<listItem listIndent="1" listStyle="default" listType="bulleted">[2.</listItem>' +719 '<listItem listIndent="2" listStyle="square" listType="bulleted">3.]</listItem>'720 );721 editor.execute( 'outdentList' );722 expect( getModelData( model ) ).to.equal(723 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +724 '<listItem listIndent="0" listStyle="circle" listType="bulleted">[2.</listItem>' +725 '<listItem listIndent="1" listStyle="square" listType="bulleted">3.]</listItem>'726 );727 } );728 it(729 'should inherit the list style attribute from parent list (outdenting many items, including the first one in the list)',730 () => {731 setModelData( model,732 '<listItem listIndent="0" listStyle="circle" listType="bulleted">[1.</listItem>' +733 '<listItem listIndent="1" listStyle="default" listType="bulleted">2.</listItem>' +734 '<listItem listIndent="2" listStyle="square" listType="bulleted">3.]</listItem>' +735 '<listItem listIndent="0" listStyle="circle" listType="bulleted">4.</listItem>'736 );737 editor.execute( 'outdentList' );738 expect( getModelData( model ) ).to.equal(739 '<paragraph>[1.</paragraph>' +740 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>' +741 '<listItem listIndent="1" listStyle="square" listType="bulleted">3.]</listItem>' +742 '<listItem listIndent="0" listStyle="circle" listType="bulleted">4.</listItem>'743 );744 }745 );746 it(747 'should inherit the list style attribute from parent list (outdenting the first item that is a parent for next list)',748 () => {749 setModelData( model,750 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.[]</listItem>' +751 '<listItem listIndent="1" listStyle="default" listType="bulleted">2.</listItem>' +752 '<listItem listIndent="2" listStyle="square" listType="bulleted">3.</listItem>' +753 '<listItem listIndent="3" listStyle="disc" listType="bulleted">4.</listItem>' +754 '<listItem listIndent="0" listStyle="circle" listType="bulleted">5.</listItem>'755 );756 editor.execute( 'outdentList' );757 expect( getModelData( model ) ).to.equal(758 '<paragraph>1.[]</paragraph>' +759 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>' +760 '<listItem listIndent="1" listStyle="square" listType="bulleted">3.</listItem>' +761 '<listItem listIndent="2" listStyle="disc" listType="bulleted">4.</listItem>' +762 '<listItem listIndent="0" listStyle="circle" listType="bulleted">5.</listItem>'763 );764 }765 );766 it( 'should not inherit the list style if outdented the only one item in the list', () => {767 setModelData( model,768 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.[]</listItem>' +769 '<listItem listIndent="1" listStyle="disc" listType="bulleted">2.</listItem>' +770 '<listItem listIndent="2" listStyle="square" listType="bulleted">3.</listItem>'771 );772 editor.execute( 'outdentList' );773 expect( getModelData( model ) ).to.equal(774 '<paragraph>1.[]</paragraph>' +775 '<listItem listIndent="0" listStyle="disc" listType="bulleted">2.</listItem>' +776 '<listItem listIndent="1" listStyle="square" listType="bulleted">3.</listItem>'777 );778 } );779 it( 'should not inherit the list style if outdented the only one item in the list (a paragraph below the list)', () => {780 setModelData( model,781 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.[]</listItem>' +782 '<listItem listIndent="1" listStyle="disc" listType="bulleted">2.</listItem>' +783 '<listItem listIndent="2" listStyle="square" listType="bulleted">3.</listItem>' +784 '<paragraph>Foo</paragraph>'785 );786 editor.execute( 'outdentList' );787 expect( getModelData( model ) ).to.equal(788 '<paragraph>1.[]</paragraph