Best JavaScript code snippet using taiko
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>' +789 '<listItem listIndent="0" listStyle="disc" listType="bulleted">2.</listItem>' +790 '<listItem listIndent="1" listStyle="square" listType="bulleted">3.</listItem>' +791 '<paragraph>Foo</paragraph>'792 );793 } );794 it( 'should not inherit the list style attribute from parent list if the `listType` is other than outdenting element', () => {795 setModelData( model,796 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +797 '<listItem listIndent="1" listStyle="decimal" listType="numbered">2.[]</listItem>' +798 '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>'799 );800 editor.execute( 'outdentList' );801 expect( getModelData( model ) ).to.equal(802 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +803 '<listItem listIndent="0" listStyle="decimal" listType="numbered">2.[]</listItem>' +804 '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>'805 );806 } );807 it( 'should not do anything if there is no list after outdenting', () => {808 setModelData( model,809 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.[]</listItem>'810 );811 editor.execute( 'outdentList' );812 expect( getModelData( model ) ).to.equal(813 '<paragraph>1.[]</paragraph>'814 );815 } );816 } );817 describe( 'indent/outdent + undo', () => {818 it( 'should use the same batch for indenting a list and updating `listType` attribute', () => {819 setModelData( model,820 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +821 '<listItem listIndent="1" listStyle="default" listType="bulleted">1A.</listItem>' +822 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2B.</listItem>' +823 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.[]</listItem>' +824 '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>'825 );826 editor.execute( 'indentList' );827 editor.execute( 'undo' );828 expect( getModelData( model ) ).to.equal(829 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +830 '<listItem listIndent="1" listStyle="default" listType="bulleted">1A.</listItem>' +831 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2B.</listItem>' +832 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.[]</listItem>' +833 '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>'834 );835 } );836 it( 'should use the same batch for outdenting a list and updating `listType` attribute', () => {837 setModelData( model,838 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +839 '<listItem listIndent="1" listStyle="default" listType="bulleted">2.[]</listItem>' +840 '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>'841 );842 editor.execute( 'outdentList' );843 editor.execute( 'undo' );844 expect( getModelData( model ) ).to.equal(845 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +846 '<listItem listIndent="1" listStyle="default" listType="bulleted">2.[]</listItem>' +847 '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>'848 );849 } );850 } );851 describe( 'delete + undo', () => {852 let editor, model, view;853 beforeEach( () => {854 return VirtualTestEditor855 .create( {856 plugins: [ Paragraph, ListStyleEditing, Typing, UndoEditing ]857 } )858 .then( newEditor => {859 editor = newEditor;860 model = editor.model;861 view = editor.editing.view;862 } );863 } );864 afterEach( () => {865 return editor.destroy();866 } );867 // See: #7930.868 it( 'should restore proper list style attribute after undo merging lists', () => {869 // â 1.870 // â 2.871 // â 3.872 // <paragraph>873 // â 1.874 // â 2.875 setModelData( model,876 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +877 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>' +878 '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>' +879 '<paragraph>[]</paragraph>' +880 '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +881 '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>'882 );883 expect( getViewData( view, { withoutSelection: true } ), 'initial data' ).to.equal(884 '<ul style="list-style-type:circle">' +885 '<li>1.</li>' +886 '<li>2.</li>' +887 '<li>3.</li>' +888 '</ul>' +889 '<p></p>' +890 '<ul style="list-style-type:square">' +891 '<li>1.</li>' +892 '<li>2.</li>' +893 '</ul>'894 );895 // After removing the paragraph.896 // â 1.897 // â 2.898 // â 3.899 // â 1.900 // â 2.901 editor.execute( 'delete' );902 expect( getViewData( view, { withoutSelection: true } ), 'executing delete' ).to.equal(903 '<ul style="list-style-type:circle">' +904 '<li>1.</li>' +905 '<li>2.</li>' +906 '<li>3.</li>' +907 '<li>1.</li>' +908 '<li>2.</li>' +909 '</ul>'910 );911 // After undo.912 // â 1.913 // â 2.914 // â 3.915 // <paragraph>916 // â 1.917 // â 2.918 editor.execute( 'undo' );919 expect( getViewData( view, { withoutSelection: true } ), 'initial data' ).to.equal(920 '<ul style="list-style-type:circle">' +921 '<li>1.</li>' +922 '<li>2.</li>' +923 '<li>3.</li>' +924 '</ul>' +925 '<p></p>' +926 '<ul style="list-style-type:square">' +927 '<li>1.</li>' +928 '<li>2.</li>' +929 '</ul>'930 );931 } );932 } );933 describe( 'todo list', () => {934 let editor, model;935 beforeEach( () => {936 return VirtualTestEditor937 .create( {938 // TodoListEditing is at the end by design. Check `ListStyleEditing.afterInit()` call.939 plugins: [ Paragraph, ListStyleEditing, TodoListEditing ]940 } )941 .then( newEditor => {942 editor = newEditor;943 model = editor.model;944 } );945 } );946 afterEach( () => {947 return editor.destroy();948 } );949 it( 'should not add the `listStyle` attribute while creating a todo list', () => {950 setModelData( model, '<paragraph>Foo[]</paragraph>' );951 editor.execute( 'todoList' );952 expect( getModelData( model ), '<listItem listIndent="0" listType="todo">Foo[]</listItem>' );953 } );954 it( 'should not add the `listStyle` attribute while switching the list type', () => {955 setModelData( model, '<listItem listIndent="0" listType="bulleted">Foo[]</listItem>' );956 editor.execute( 'todoList' );957 expect( getModelData( model ), '<listItem listIndent="0" listType="todo">Foo[]</listItem>' );958 } );959 it( 'should remove the `listStyle` attribute while switching the list type that uses the list style feature', () => {960 setModelData( model, '<listItem listIndent="0" listType="bulleted" listStyle="circle">Foo[]</listItem>' );961 editor.execute( 'todoList' );962 expect( getModelData( model ), '<listItem listIndent="0" listType="todo">Foo[]</listItem>' );963 } );964 it( 'should not inherit the list style attribute when inserting a todo list item', () => {965 setModelData( model,966 '<paragraph>Foo Bar.[]</paragraph>' +967 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +968 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'969 );970 editor.execute( 'todoList' );971 expect( getModelData( model ) ).to.equal(972 '<listItem listIndent="0" listType="todo">Foo Bar.[]</listItem>' +973 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +974 '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'975 );976 } );977 } );978 describe( 'removing content between two lists', () => {979 let editor, model;980 beforeEach( () => {981 return VirtualTestEditor982 .create( {983 plugins: [ Paragraph, ListStyleEditing, Typing ]984 } )985 .then( newEditor => {986 editor = newEditor;987 model = editor.model;988 } );989 } );990 afterEach( () => {991 return editor.destroy();992 } );993 it( 'should not do anything while removing a letter inside a listItem', () => {994 setModelData( model,995 '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +996 '<listItem listIndent="0" listStyle="square" listType="bulleted">2.[]</listItem>' +997 '<paragraph></paragraph>' +998 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +999 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'1000 );1001 editor.execute( 'delete' );1002 expect( getModelData( model ) ).to.equal(1003 '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +1004 '<listItem listIndent="0" listStyle="square" listType="bulleted">2[]</listItem>' +1005 '<paragraph></paragraph>' +1006 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +1007 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'1008 );1009 } );1010 it( 'should not do anything if there is a non-listItem before the removed content', () => {1011 setModelData( model,1012 '<paragraph>Foo</paragraph>' +1013 '<paragraph>[]</paragraph>' +1014 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +1015 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'1016 );1017 editor.execute( 'delete' );1018 expect( getModelData( model ) ).to.equal(1019 '<paragraph>Foo[]</paragraph>' +1020 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +1021 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'1022 );1023 } );1024 it( 'should not do anything if there is a non-listItem after the removed content', () => {1025 setModelData( model,1026 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +1027 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>' +1028 '<paragraph>[]</paragraph>' +1029 '<paragraph>Foo</paragraph>'1030 );1031 editor.execute( 'delete' );1032 expect( getModelData( model ) ).to.equal(1033 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +1034 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.[]</listItem>' +1035 '<paragraph>Foo</paragraph>'1036 );1037 } );1038 it( 'should not do anything if there is no element after the removed content', () => {1039 setModelData( model,1040 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +1041 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>' +1042 '<paragraph>[]</paragraph>'1043 );1044 editor.execute( 'delete' );1045 expect( getModelData( model ) ).to.equal(1046 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +1047 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.[]</listItem>'1048 );1049 } );1050 it(1051 'should modify the the `listStyle` attribute for the merged (second) list when removing content between those lists',1052 () => {1053 setModelData( model,1054 '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +1055 '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +1056 '<paragraph>[]</paragraph>' +1057 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +1058 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'1059 );1060 editor.execute( 'delete' );1061 expect( getModelData( model ) ).to.equal(1062 '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +1063 '<listItem listIndent="0" listStyle="square" listType="bulleted">2.[]</listItem>' +1064 '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +1065 '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>'1066 );1067 }1068 );1069 it( 'should read the `listStyle` attribute from the most outer list', () => {1070 setModelData( model,1071 '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +1072 '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +1073 '<listItem listIndent="1" listStyle="decimal" listType="numbered">2.1.</listItem>' +1074 '<listItem listIndent="2" listStyle="default" listType="numbered">2.1.1</listItem>' +1075 '<paragraph>[]</paragraph>' +1076 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +1077 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'1078 );1079 editor.execute( 'delete' );1080 expect( getModelData( model ) ).to.equal(1081 '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +1082 '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +1083 '<listItem listIndent="1" listStyle="decimal" listType="numbered">2.1.</listItem>' +1084 '<listItem listIndent="2" listStyle="default" listType="numbered">2.1.1[]</listItem>' +1085 '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +1086 '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>'1087 );1088 } );1089 it(1090 'should not modify the the `listStyle` attribute for the merged (second) list if merging different `listType` attribute',1091 () => {1092 setModelData( model,1093 '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +1094 '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +1095 '<paragraph>[]</paragraph>' +1096 '<listItem listIndent="0" listStyle="decimal" listType="numbered">1.</listItem>' +1097 '<listItem listIndent="0" listStyle="decimal" listType="numbered">2.</listItem>'1098 );1099 editor.execute( 'delete' );1100 expect( getModelData( model ) ).to.equal(1101 '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +1102 '<listItem listIndent="0" listStyle="square" listType="bulleted">2.[]</listItem>' +1103 '<listItem listIndent="0" listStyle="decimal" listType="numbered">1.</listItem>' +1104 '<listItem listIndent="0" listStyle="decimal" listType="numbered">2.</listItem>'1105 );1106 }1107 );1108 it(1109 'should modify the the `listStyle` attribute for the merged (second) list when removing content from both lists',1110 () => {1111 setModelData( model,1112 '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +1113 '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +1114 '<listItem listIndent="0" listStyle="square" listType="bulleted">[3.</listItem>' +1115 '<paragraph>Foo</paragraph>' +1116 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.]</listItem>' +1117 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'1118 );1119 editor.execute( 'delete' );1120 expect( getModelData( model ) ).to.equal(1121 '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +1122 '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +1123 '<listItem listIndent="0" listStyle="square" listType="bulleted">[]</listItem>' +1124 '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>'1125 );1126 }1127 );1128 it(1129 'should modify the the `listStyle` attribute for the merged (second) list when typing over content from both lists',1130 () => {1131 setModelData( model,1132 '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +1133 '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +1134 '<listItem listIndent="0" listStyle="square" listType="bulleted">[3.</listItem>' +1135 '<paragraph>Foo</paragraph>' +1136 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.]</listItem>' +1137 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'1138 );1139 editor.execute( 'input', { text: 'Foo' } );1140 expect( getModelData( model ) ).to.equal(1141 '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +1142 '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +1143 '<listItem listIndent="0" listStyle="square" listType="bulleted">Foo[]</listItem>' +1144 '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>'1145 );1146 }1147 );1148 it(1149 'should not modify the the `listStyle` if lists were not merged but the content was partially removed',1150 () => {1151 setModelData( model,1152 '<listItem listIndent="0" listStyle="square" listType="bulleted">111.</listItem>' +1153 '<listItem listIndent="0" listStyle="square" listType="bulleted">222.</listItem>' +1154 '<listItem listIndent="0" listStyle="square" listType="bulleted">[333.</listItem>' +1155 '<paragraph>Foo</paragraph>' +1156 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1]11.</listItem>' +1157 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'1158 );1159 editor.execute( 'delete' );1160 expect( getModelData( model ) ).to.equal(1161 '<listItem listIndent="0" listStyle="square" listType="bulleted">111.</listItem>' +1162 '<listItem listIndent="0" listStyle="square" listType="bulleted">222.</listItem>' +1163 '<listItem listIndent="0" listStyle="circle" listType="bulleted">[]11.</listItem>' +1164 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'1165 );1166 }1167 );1168 it( 'should not do anything while typing in a list item', () => {1169 setModelData( model,1170 '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +1171 '<listItem listIndent="0" listStyle="square" listType="bulleted">2.[]</listItem>' +1172 '<listItem listIndent="0" listStyle="square" listType="bulleted">3.</listItem>' +1173 '<paragraph></paragraph>' +1174 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +1175 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'1176 );1177 const modelChangeStub = sinon.stub( model, 'change' ).callThrough();1178 simulateTyping( ' Foo' );1179 // Each character calls `editor.model.change()`.1180 expect( modelChangeStub.callCount ).to.equal( 4 );1181 expect( getModelData( model ) ).to.equal(1182 '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +1183 '<listItem listIndent="0" listStyle="square" listType="bulleted">2. Foo[]</listItem>' +1184 '<listItem listIndent="0" listStyle="square" listType="bulleted">3.</listItem>' +1185 '<paragraph></paragraph>' +1186 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +1187 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'1188 );1189 } );1190 // See: #8073.1191 it( 'should not crash when removing a content between intended lists', () => {1192 setModelData( model,1193 '<listItem listIndent="0" listStyle="default" listType="bulleted">aaaa</listItem>' +1194 '<listItem listIndent="1" listStyle="default" listType="bulleted">bb[bb</listItem>' +1195 '<listItem listIndent="2" listStyle="default" listType="bulleted">cc]cc</listItem>' +1196 '<listItem listIndent="3" listStyle="default" listType="bulleted">dddd</listItem>'1197 );1198 editor.execute( 'delete' );1199 expect( getModelData( model ) ).to.equal(1200 '<listItem listIndent="0" listStyle="default" listType="bulleted">aaaa</listItem>' +1201 '<listItem listIndent="1" listStyle="default" listType="bulleted">bb[]cc</listItem>' +1202 '<listItem listIndent="2" listStyle="default" listType="bulleted">dddd</listItem>'1203 );1204 } );1205 it( 'should read the `listStyle` attribute from the most outer selected list while removing content between lists', () => {1206 setModelData( model,1207 '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +1208 '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +1209 '<listItem listIndent="1" listStyle="decimal" listType="numbered">2.1.</listItem>' +1210 '<listItem listIndent="2" listStyle="lower-latin" listType="numbered">2.1.1[foo</listItem>' +1211 '<paragraph>Foo</paragraph>' +1212 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +1213 '<listItem listIndent="1" listStyle="circle" listType="bulleted">bar]2.</listItem>'1214 );1215 editor.execute( 'delete' );1216 expect( getModelData( model ) ).to.equal(1217 '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +1218 '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +1219 '<listItem listIndent="1" listStyle="decimal" listType="numbered">2.1.</listItem>' +1220 '<listItem listIndent="2" listStyle="lower-latin" listType="numbered">2.1.1[]2.</listItem>'1221 );1222 } );1223 function simulateTyping( text ) {1224 // While typing, every character is an atomic change.1225 text.split( '' ).forEach( character => {1226 editor.execute( 'input', {1227 text: character1228 } );1229 } );1230 }1231 } );1232 } );...
listcommand.js
Source:listcommand.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 Editor from '@ckeditor/ckeditor5-core/src/editor/editor';6import Model from '@ckeditor/ckeditor5-engine/src/model/model';7import ListCommand from '../src/listcommand';8import { setData, getData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';9describe( 'ListCommand', () => {10 let editor, command, model, doc, root;11 beforeEach( () => {12 editor = new Editor();13 editor.model = new Model();14 model = editor.model;15 doc = model.document;16 root = doc.createRoot();17 command = new ListCommand( editor, 'bulleted' );18 model.schema.register( 'listItem', {19 inheritAllFrom: '$block',20 allowAttributes: [ 'listType', 'listIndent' ]21 } );22 model.schema.register( 'paragraph', {23 inheritAllFrom: '$block',24 allowIn: 'widget'25 } );26 model.schema.register( 'widget', { inheritAllFrom: '$block' } );27 model.schema.addChildCheck( ( ctx, childDef ) => {28 if ( ctx.endsWith( 'widget' ) && childDef.name == 'listItem' ) {29 return false;30 }31 } );32 setData(33 model,34 '<paragraph>foo</paragraph>' +35 '<listItem listType="bulleted" listIndent="0">bulleted</listItem>' +36 '<listItem listType="numbered" listIndent="0">numbered</listItem>' +37 '<paragraph>bar</paragraph>' +38 '<widget>' +39 '<paragraph>xyz</paragraph>' +40 '</widget>'41 );42 model.change( writer => {43 writer.setSelection( doc.getRoot().getChild( 0 ), 0 );44 } );45 } );46 afterEach( () => {47 command.destroy();48 } );49 describe( 'ListCommand', () => {50 describe( 'constructor()', () => {51 it( 'should create list command with given type and value set to false', () => {52 expect( command.type ).to.equal( 'bulleted' );53 expect( command.value ).to.be.false;54 const numberedList = new ListCommand( editor, 'numbered' );55 expect( numberedList.type ).to.equal( 'numbered' );56 } );57 } );58 describe( 'value', () => {59 it( 'should be false if first position in selection is not in a list item', () => {60 model.change( writer => {61 writer.setSelection( doc.getRoot().getChild( 3 ), 0 );62 } );63 expect( command.value ).to.be.false;64 } );65 it( 'should be false if first position in selection is in a list item of different type', () => {66 model.change( writer => {67 writer.setSelection( doc.getRoot().getChild( 2 ), 0 );68 } );69 expect( command.value ).to.be.false;70 } );71 it( 'should be true if first position in selection is in a list item of same type', () => {72 model.change( writer => {73 writer.setSelection( doc.getRoot().getChild( 1 ), 0 );74 } );75 expect( command.value ).to.be.true;76 } );77 } );78 describe( 'isEnabled', () => {79 it( 'should be true if entire selection is in a list', () => {80 setData( model, '<listItem listType="bulleted" listIndent="0">[a]</listItem>' );81 expect( command.isEnabled ).to.be.true;82 } );83 it( 'should be true if entire selection is in a block which can be turned into a list', () => {84 setData( model, '<paragraph>[a]</paragraph>' );85 expect( command.isEnabled ).to.be.true;86 } );87 it( 'should be true if selection first position is in a block which can be turned into a list', () => {88 setData( model, '<paragraph>[a</paragraph><widget>b]</widget>' );89 expect( command.isEnabled ).to.be.true;90 } );91 it( 'should be false if selection first position is in an element which cannot be converted to a list item', () => {92 setData( model, '<widget><paragraph>[a</paragraph></widget><paragraph>b]</paragraph>' );93 expect( command.isEnabled ).to.be.false;94 } );95 it( 'should be false in a root which does not allow blocks at all', () => {96 doc.createRoot( 'paragraph', 'inlineOnlyRoot' );97 setData( model, 'a[]b', { rootName: 'inlineOnlyRoot' } );98 expect( command.isEnabled ).to.be.false;99 } );100 } );101 describe( 'execute()', () => {102 it( 'should use parent batch', () => {103 model.change( writer => {104 expect( writer.batch.operations.length ).to.equal( 0 );105 command.execute();106 expect( writer.batch.operations.length ).to.be.above( 0 );107 } );108 } );109 describe( 'collapsed selection', () => {110 it( 'should rename closest block to listItem and set correct attributes', () => {111 setData( model, '<paragraph>fo[]o</paragraph>' );112 command.execute();113 expect( getData( model ) ).to.equal( '<listItem listIndent="0" listType="bulleted">fo[]o</listItem>' );114 } );115 it( 'should rename closest listItem to paragraph', () => {116 setData( model, '<listItem listIndent="0" listType="bulleted">fo[]o</listItem>' );117 command.execute();118 // Attributes will be removed by post fixer.119 expect( getData( model ) ).to.equal( '<paragraph listIndent="0" listType="bulleted">fo[]o</paragraph>' );120 } );121 it( 'should change closest listItem\' type', () => {122 setData( model, '<listItem listIndent="0" listType="numbered">fo[]o</listItem>' );123 command.execute();124 expect( getData( model ) ).to.equal( '<listItem listIndent="0" listType="bulleted">fo[]o</listItem>' );125 } );126 it( 'should handle outdenting sub-items when list item is turned off', () => {127 /* eslint-disable max-len */128 // Taken from docs.129 //130 // 1 * --------131 // 2 * --------132 // 3 * -------- <- this is turned off.133 // 4 * -------- <- this has to become indent = 0, because it will be first item on a new list.134 // 5 * -------- <- this should be still be a child of item above, so indent = 1.135 // 6 * -------- <- this also has to become indent = 0, because it shouldn't end up as a child of any of items above.136 // 7 * -------- <- this should be still be a child of item above, so indent = 1.137 // 8 * -------- <- this has to become indent = 0.138 // 9 * -------- <- this should still be a child of item above, so indent = 1.139 // 10 * -------- <- this should still be a child of item above, so indent = 2.140 // 11 * -------- <- this should still be at the same level as item above, so indent = 2.141 // 12 * -------- <- this and all below are left unchanged.142 // 13 * --------143 // 14 * --------144 //145 // After turning off "3", the list becomes:146 //147 // 1 * --------148 // 2 * --------149 //150 // 3 --------151 //152 // 4 * --------153 // 5 * --------154 // 6 * --------155 // 7 * --------156 // 8 * --------157 // 9 * --------158 // 10 * --------159 // 11 * --------160 // 12 * --------161 // 13 * --------162 // 14 * --------163 /* eslint-enable max-len */164 setData(165 model,166 '<listItem listIndent="0" listType="bulleted">---</listItem>' +167 '<listItem listIndent="1" listType="bulleted">---</listItem>' +168 '<listItem listIndent="2" listType="bulleted">[]---</listItem>' +169 '<listItem listIndent="3" listType="bulleted">---</listItem>' +170 '<listItem listIndent="4" listType="bulleted">---</listItem>' +171 '<listItem listIndent="2" listType="bulleted">---</listItem>' +172 '<listItem listIndent="3" listType="bulleted">---</listItem>' +173 '<listItem listIndent="1" listType="bulleted">---</listItem>' +174 '<listItem listIndent="2" listType="bulleted">---</listItem>' +175 '<listItem listIndent="3" listType="bulleted">---</listItem>' +176 '<listItem listIndent="3" listType="bulleted">---</listItem>' +177 '<listItem listIndent="0" listType="bulleted">---</listItem>' +178 '<listItem listIndent="1" listType="bulleted">---</listItem>' +179 '<listItem listIndent="2" listType="bulleted">---</listItem>'180 );181 command.execute();182 const expectedData =183 '<listItem listIndent="0" listType="bulleted">---</listItem>' +184 '<listItem listIndent="1" listType="bulleted">---</listItem>' +185 '<paragraph listIndent="2" listType="bulleted">[]---</paragraph>' + // Attributes will be removed by post fixer.186 '<listItem listIndent="0" listType="bulleted">---</listItem>' +187 '<listItem listIndent="1" listType="bulleted">---</listItem>' +188 '<listItem listIndent="0" listType="bulleted">---</listItem>' +189 '<listItem listIndent="1" listType="bulleted">---</listItem>' +190 '<listItem listIndent="0" listType="bulleted">---</listItem>' +191 '<listItem listIndent="1" listType="bulleted">---</listItem>' +192 '<listItem listIndent="2" listType="bulleted">---</listItem>' +193 '<listItem listIndent="2" listType="bulleted">---</listItem>' +194 '<listItem listIndent="0" listType="bulleted">---</listItem>' +195 '<listItem listIndent="1" listType="bulleted">---</listItem>' +196 '<listItem listIndent="2" listType="bulleted">---</listItem>';197 expect( getData( model ) ).to.equal( expectedData );198 } );199 } );200 describe( 'non-collapsed selection', () => {201 beforeEach( () => {202 setData(203 model,204 '<listItem listIndent="0" listType="bulleted">---</listItem>' +205 '<listItem listIndent="0" listType="bulleted">---</listItem>' +206 '<paragraph>---</paragraph>' +207 '<paragraph>---</paragraph>' +208 '<listItem listIndent="0" listType="numbered">---</listItem>' +209 '<listItem listIndent="0" listType="numbered">---</listItem>' +210 '<listItem listIndent="1" listType="bulleted">---</listItem>' +211 '<listItem listIndent="2" listType="bulleted">---</listItem>'212 );213 } );214 // https://github.com/ckeditor/ckeditor5-list/issues/62215 it( 'should not rename blocks which cannot become listItems (list item is not allowed in their parent)', () => {216 model.schema.register( 'restricted' );217 model.schema.extend( 'restricted', { allowIn: '$root' } );218 model.schema.register( 'fooBlock', { inheritAllFrom: '$block' } );219 model.schema.extend( 'fooBlock', { allowIn: 'restricted' } );220 setData(221 model,222 '<paragraph>a[bc</paragraph>' +223 '<restricted><fooBlock></fooBlock></restricted>' +224 '<paragraph>de]f</paragraph>'225 );226 command.execute();227 expect( getData( model ) ).to.equal(228 '<listItem listIndent="0" listType="bulleted">a[bc</listItem>' +229 '<restricted><fooBlock></fooBlock></restricted>' +230 '<listItem listIndent="0" listType="bulleted">de]f</listItem>'231 );232 } );233 it( 'should not rename blocks which cannot become listItems (block is an object)', () => {234 model.schema.register( 'image', {235 isBlock: true,236 isObject: true,237 allowIn: '$root'238 } );239 setData(240 model,241 '<paragraph>a[bc</paragraph>' +242 '<image></image>' +243 '<paragraph>de]f</paragraph>'244 );245 command.execute();246 expect( getData( model ) ).to.equal(247 '<listItem listIndent="0" listType="bulleted">a[bc</listItem>' +248 '<image></image>' +249 '<listItem listIndent="0" listType="bulleted">de]f</listItem>'250 );251 } );252 it( 'should rename closest block to listItem and set correct attributes', () => {253 // From first paragraph to second paragraph.254 // Command value=false, we are turning on list items.255 model.change( writer => {256 writer.setSelection( writer.createRange(257 writer.createPositionAt( root.getChild( 2 ), 0 ),258 writer.createPositionAt( root.getChild( 3 ), 'end' )259 ) );260 } );261 command.execute();262 const expectedData =263 '<listItem listIndent="0" listType="bulleted">---</listItem>' +264 '<listItem listIndent="0" listType="bulleted">---</listItem>' +265 '<listItem listIndent="0" listType="bulleted">[---</listItem>' +266 '<listItem listIndent="0" listType="bulleted">---]</listItem>' +267 '<listItem listIndent="0" listType="numbered">---</listItem>' +268 '<listItem listIndent="0" listType="numbered">---</listItem>' +269 '<listItem listIndent="1" listType="bulleted">---</listItem>' +270 '<listItem listIndent="2" listType="bulleted">---</listItem>';271 expect( getData( model ) ).to.equal( expectedData );272 } );273 it( 'should rename closest listItem to paragraph', () => {274 // From second bullet list item to first numbered list item.275 // Command value=true, we are turning off list items.276 model.change( writer => {277 writer.setSelection( writer.createRange(278 writer.createPositionAt( root.getChild( 1 ), 0 ),279 writer.createPositionAt( root.getChild( 4 ), 'end' )280 ) );281 } );282 // Convert paragraphs, leave numbered list items.283 command.execute();284 const expectedData =285 '<listItem listIndent="0" listType="bulleted">---</listItem>' +286 '<paragraph listIndent="0" listType="bulleted">[---</paragraph>' + // Attributes will be removed by post fixer.287 '<paragraph>---</paragraph>' +288 '<paragraph>---</paragraph>' +289 '<paragraph listIndent="0" listType="numbered">---]</paragraph>' + // Attributes will be removed by post fixer.290 '<listItem listIndent="0" listType="numbered">---</listItem>' +291 '<listItem listIndent="1" listType="bulleted">---</listItem>' +292 '<listItem listIndent="2" listType="bulleted">---</listItem>';293 expect( getData( model ) ).to.equal( expectedData );294 } );295 it( 'should change closest listItem\'s type', () => {296 // From first numbered lsit item to third bulleted list item.297 model.change( writer => {298 writer.setSelection( writer.createRange(299 writer.createPositionAt( root.getChild( 4 ), 0 ),300 writer.createPositionAt( root.getChild( 6 ), 0 )301 ) );302 } );303 // Convert paragraphs, leave numbered list items.304 command.execute();305 const expectedData =306 '<listItem listIndent="0" listType="bulleted">---</listItem>' +307 '<listItem listIndent="0" listType="bulleted">---</listItem>' +308 '<paragraph>---</paragraph>' +309 '<paragraph>---</paragraph>' +310 '<listItem listIndent="0" listType="bulleted">[---</listItem>' +311 '<listItem listIndent="0" listType="bulleted">---</listItem>' +312 '<listItem listIndent="1" listType="bulleted">]---</listItem>' +313 '<listItem listIndent="2" listType="bulleted">---</listItem>';314 expect( getData( model ) ).to.equal( expectedData );315 } );316 it( 'should handle outdenting sub-items when list item is turned off', () => {317 // From first numbered list item to third bulleted list item.318 model.change( writer => {319 writer.setSelection( writer.createRange(320 writer.createPositionAt( root.getChild( 1 ), 0 ),321 writer.createPositionAt( root.getChild( 5 ), 'end' )322 ) );323 } );324 // Convert paragraphs, leave numbered list items.325 command.execute();326 const expectedData =327 '<listItem listIndent="0" listType="bulleted">---</listItem>' +328 '<paragraph listIndent="0" listType="bulleted">[---</paragraph>' + // Attributes will be removed by post fixer.329 '<paragraph>---</paragraph>' +330 '<paragraph>---</paragraph>' +331 '<paragraph listIndent="0" listType="numbered">---</paragraph>' + // Attributes will be removed by post fixer.332 '<paragraph listIndent="0" listType="numbered">---]</paragraph>' + // Attributes will be removed by post fixer.333 '<listItem listIndent="0" listType="bulleted">---</listItem>' +334 '<listItem listIndent="1" listType="bulleted">---</listItem>';335 expect( getData( model ) ).to.equal( expectedData );336 } );337 // Example from docs.338 it( 'should change type of all items in nested list if one of items changed', () => {339 setData(340 model,341 '<listItem listIndent="0" listType="numbered">---</listItem>' +342 '<listItem listIndent="1" listType="numbered">---</listItem>' +343 '<listItem listIndent="2" listType="numbered">---</listItem>' +344 '<listItem listIndent="1" listType="numbered">---</listItem>' +345 '<listItem listIndent="2" listType="numbered">---</listItem>' +346 '<listItem listIndent="2" listType="numbered">-[-</listItem>' +347 '<listItem listIndent="1" listType="numbered">---</listItem>' +348 '<listItem listIndent="1" listType="numbered">---</listItem>' +349 '<listItem listIndent="0" listType="numbered">---</listItem>' +350 '<listItem listIndent="1" listType="numbered">-]-</listItem>' +351 '<listItem listIndent="1" listType="numbered">---</listItem>' +352 '<listItem listIndent="2" listType="numbered">---</listItem>' +353 '<listItem listIndent="0" listType="numbered">---</listItem>'354 );355 // * ------ <-- do not fix, top level item356 // * ------ <-- fix, because latter list item of this item's list is changed357 // * ------ <-- do not fix, item is not affected (different list)358 // * ------ <-- fix, because latter list item of this item's list is changed359 // * ------ <-- fix, because latter list item of this item's list is changed360 // * ---[-- <-- already in selection361 // * ------ <-- already in selection362 // * ------ <-- already in selection363 // * ------ <-- already in selection, but does not cause other list items to change because is top-level364 // * ---]-- <-- already in selection365 // * ------ <-- fix, because preceding list item of this item's list is changed366 // * ------ <-- do not fix, item is not affected (different list)367 // * ------ <-- do not fix, top level item368 command.execute();369 const expectedData =370 '<listItem listIndent="0" listType="numbered">---</listItem>' +371 '<listItem listIndent="1" listType="bulleted">---</listItem>' +372 '<listItem listIndent="2" listType="numbered">---</listItem>' +373 '<listItem listIndent="1" listType="bulleted">---</listItem>' +374 '<listItem listIndent="2" listType="bulleted">---</listItem>' +375 '<listItem listIndent="2" listType="bulleted">-[-</listItem>' +376 '<listItem listIndent="1" listType="bulleted">---</listItem>' +377 '<listItem listIndent="1" listType="bulleted">---</listItem>' +378 '<listItem listIndent="0" listType="bulleted">---</listItem>' +379 '<listItem listIndent="1" listType="bulleted">-]-</listItem>' +380 '<listItem listIndent="1" listType="bulleted">---</listItem>' +381 '<listItem listIndent="2" listType="numbered">---</listItem>' +382 '<listItem listIndent="0" listType="numbered">---</listItem>';383 expect( getData( model ) ).to.equal( expectedData );384 } );385 } );386 it( 'should fire "_executeCleanup" event after finish all operations with all changed items', done => {387 setData( model,388 '<paragraph>Foo 1.</paragraph>' +389 '<paragraph>[Foo 2.</paragraph>' +390 '<paragraph>Foo 3.]</paragraph>' +391 '<paragraph>Foo 4.</paragraph>'392 );393 command.execute();394 expect( getData( model ) ).to.equal(395 '<paragraph>Foo 1.</paragraph>' +396 '<listItem listIndent="0" listType="bulleted">[Foo 2.</listItem>' +397 '<listItem listIndent="0" listType="bulleted">Foo 3.]</listItem>' +398 '<paragraph>Foo 4.</paragraph>'399 );400 command.on( '_executeCleanup', ( evt, data ) => {401 expect( data ).to.deep.equal( [402 root.getChild( 2 ),403 root.getChild( 1 )404 ] );405 done();406 } );407 command.execute();408 } );409 } );410 } );...
utils.js
Source:utils.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 ViewContainerElement from '@ckeditor/ckeditor5-engine/src/view/containerelement';6import ViewDowncastWriter from '@ckeditor/ckeditor5-engine/src/view/downcastwriter';7import { setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';8import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';9import ListEditing from '../src/listediting';10import ListStyleEditing from '../src/liststyleediting';11import { createViewListItemElement, getSiblingListItem, getSiblingNodes } from '../src/utils';12describe( 'utils', () => {13 let writer;14 beforeEach( () => {15 writer = new ViewDowncastWriter( {} );16 } );17 describe( 'createViewListItemElement()', () => {18 it( 'should create ViewContainerElement', () => {19 const item = createViewListItemElement( writer );20 expect( item ).to.be.instanceof( ViewContainerElement );21 } );22 it( 'should have li name', () => {23 const item = createViewListItemElement( writer );24 expect( item.name ).to.equal( 'li' );25 } );26 describe( 'getFillerOffset', () => {27 it( 'should return 0 if item is empty', () => {28 const item = createViewListItemElement( writer );29 expect( item.getFillerOffset() ).to.equal( 0 );30 } );31 it( 'should return 0 if item has only lists as children', () => {32 const innerListItem1 = createViewListItemElement( writer );33 writer.insert(34 writer.createPositionAt( innerListItem1, 0 ),35 writer.createText( 'foo' )36 );37 const innerListItem2 = createViewListItemElement( writer );38 writer.insert(39 writer.createPositionAt( innerListItem2, 0 ),40 writer.createText( 'bar' )41 );42 const innerList = writer.createContainerElement( 'ul' );43 writer.insert( writer.createPositionAt( innerList, 0 ), innerListItem1 );44 writer.insert( writer.createPositionAt( innerList, 0 ), innerListItem2 );45 const outerListItem = createViewListItemElement( writer );46 writer.insert( writer.createPositionAt( outerListItem, 0 ), innerList );47 expect( outerListItem.getFillerOffset() ).to.equal( 0 );48 } );49 it( 'should return null if item has non-list contents', () => {50 const item = createViewListItemElement( writer );51 writer.insert(52 writer.createPositionAt( item, 0 ),53 writer.createText( 'foo' )54 );55 expect( item.getFillerOffset() ).to.be.null;56 } );57 // Block filler is required after the `<br>` element if the element is the last child in the container.58 // See: https://github.com/ckeditor/ckeditor5/issues/1312#issuecomment-436669045.59 describe( 'for <br> elements in container', () => {60 it( 'returns offset of the last child which is the <br> element (1)', () => {61 const item = createViewListItemElement( writer );62 writer.insert( writer.createPositionAt( item, 0 ), writer.createEmptyElement( 'br' ) );63 expect( item.getFillerOffset() ).to.equal( 1 );64 } );65 it( 'returns offset of the last child which is the <br> element (2)', () => {66 const item = createViewListItemElement( writer );67 writer.insert( writer.createPositionAt( item, 0 ), writer.createEmptyElement( 'br' ) );68 writer.insert( writer.createPositionAt( item, 1 ), writer.createEmptyElement( 'br' ) );69 expect( item.getFillerOffset() ).to.equal( 2 );70 } );71 it( 'always returns the last <br> element in the container', () => {72 const item = createViewListItemElement( writer );73 writer.insert( writer.createPositionAt( item, 0 ), writer.createText( 'foo' ) );74 writer.insert( writer.createPositionAt( item, 1 ), writer.createEmptyElement( 'br' ) );75 writer.insert( writer.createPositionAt( item, 2 ), writer.createEmptyElement( 'br' ) );76 expect( item.getFillerOffset() ).to.equal( 3 );77 } );78 it( 'works fine with non-empty container with multi <br> elements', () => {79 const item = createViewListItemElement( writer );80 writer.insert( writer.createPositionAt( item, 0 ), writer.createText( 'foo' ) );81 writer.insert( writer.createPositionAt( item, 1 ), writer.createEmptyElement( 'br' ) );82 writer.insert( writer.createPositionAt( item, 2 ), writer.createText( 'bar' ) );83 writer.insert( writer.createPositionAt( item, 3 ), writer.createEmptyElement( 'br' ) );84 expect( item.getFillerOffset() ).to.equal( 4 );85 } );86 it( 'ignores the ui elements', () => {87 const item = createViewListItemElement( writer );88 writer.insert( writer.createPositionAt( item, 0 ), writer.createUIElement( 'span' ) );89 writer.insert( writer.createPositionAt( item, 1 ), writer.createEmptyElement( 'br' ) );90 expect( item.getFillerOffset() ).to.equal( 2 );91 } );92 it( 'empty element must be the <br> element', () => {93 const item = createViewListItemElement( writer );94 writer.insert(95 writer.createPositionAt( item, 0 ),96 writer.createEmptyElement( 'img' )97 );98 expect( item.getFillerOffset() ).to.be.null;99 } );100 } );101 } );102 } );103 describe( 'getSiblingListItem()', () => {104 let editor, model, document;105 beforeEach( () => {106 return VirtualTestEditor.create( { plugins: [ ListEditing ] } )107 .then( newEditor => {108 editor = newEditor;109 model = editor.model;110 document = model.document;111 } );112 } );113 afterEach( () => {114 return editor.destroy();115 } );116 it( 'should return the passed element if it matches the criteria (sameIndent, listIndent=0)', () => {117 setData( model,118 '<listItem listType="bulleted" listIndent="0">0.</listItem>' +119 '<listItem listType="bulleted" listIndent="0">1.</listItem>' + // Starting item, wanted item.120 '<listItem listType="bulleted" listIndent="0">2.</listItem>'121 );122 const listItem = document.getRoot().getChild( 1 );123 const foundElement = getSiblingListItem( listItem, {124 sameIndent: true,125 listIndent: 0126 } );127 expect( foundElement ).to.equal( document.getRoot().getChild( 1 ) );128 } );129 it( 'should return the passed element if it matches the criteria (sameIndent, listIndent=0, direction="forward")', () => {130 setData( model,131 '<listItem listType="bulleted" listIndent="0">0.</listItem>' +132 '<listItem listType="bulleted" listIndent="0">1.</listItem>' + // Starting item, wanted item.133 '<listItem listType="bulleted" listIndent="0">2.</listItem>'134 );135 const listItem = document.getRoot().getChild( 1 );136 const foundElement = getSiblingListItem( listItem, {137 sameIndent: true,138 listIndent: 0,139 direction: 'forward'140 } );141 expect( foundElement ).to.equal( document.getRoot().getChild( 1 ) );142 } );143 it( 'should return the first listItem that matches criteria (sameIndent, listIndent=1)', () => {144 setData( model,145 '<listItem listType="bulleted" listIndent="0">0.</listItem>' +146 '<listItem listType="bulleted" listIndent="0">1.</listItem>' +147 '<listItem listType="bulleted" listIndent="1">1.1</listItem>' +148 '<listItem listType="bulleted" listIndent="1">1.2</listItem>' + // Wanted item.149 '<listItem listType="bulleted" listIndent="0">2.</listItem>' + // Starting item.150 '<listItem listType="bulleted" listIndent="1">2.1.</listItem>' +151 '<listItem listType="bulleted" listIndent="1">2.2.</listItem>'152 );153 const listItem = document.getRoot().getChild( 5 );154 const foundElement = getSiblingListItem( listItem.previousSibling, {155 sameIndent: true,156 listIndent: 1157 } );158 expect( foundElement ).to.equal( document.getRoot().getChild( 3 ) );159 } );160 it( 'should return the first listItem that matches criteria (sameIndent, listIndent=1, direction="forward")', () => {161 setData( model,162 '<listItem listType="bulleted" listIndent="0">0.</listItem>' +163 '<listItem listType="bulleted" listIndent="0">1.</listItem>' + // Starting item.164 '<listItem listType="bulleted" listIndent="0">2.</listItem>' +165 '<listItem listType="bulleted" listIndent="1">2.1.</listItem>' + // Wanted item.166 '<listItem listType="bulleted" listIndent="1">2.2.</listItem>'167 );168 const listItem = document.getRoot().getChild( 1 );169 const foundElement = getSiblingListItem( listItem.nextSibling, {170 sameIndent: true,171 listIndent: 1,172 direction: 'forward'173 } );174 expect( foundElement ).to.equal( document.getRoot().getChild( 3 ) );175 } );176 it( 'should return the first listItem that matches criteria (smallerIndent, listIndent=1)', () => {177 setData( model,178 '<listItem listType="bulleted" listIndent="0">0.</listItem>' +179 '<listItem listType="bulleted" listIndent="0">1.</listItem>' +180 '<listItem listType="bulleted" listIndent="0">2.</listItem>' + // Wanted item.181 '<listItem listType="bulleted" listIndent="1">2.1.</listItem>' + // Starting item.182 '<listItem listType="bulleted" listIndent="1">2.2.</listItem>'183 );184 const listItem = document.getRoot().getChild( 4 );185 const foundElement = getSiblingListItem( listItem, {186 smallerIndent: true,187 listIndent: 1188 } );189 expect( foundElement ).to.equal( document.getRoot().getChild( 2 ) );190 } );191 it( 'should return the first listItem that matches criteria (smallerIndent, listIndent=1, direction="forward")', () => {192 setData( model,193 '<listItem listType="bulleted" listIndent="0">0.</listItem>' +194 '<listItem listType="bulleted" listIndent="1">0.1.</listItem>' + // Starting item.195 '<listItem listType="bulleted" listIndent="1">0.2.</listItem>' +196 '<listItem listType="bulleted" listIndent="1">0.3.</listItem>' +197 '<listItem listType="bulleted" listIndent="0">1.</listItem>' // Wanted item.198 );199 const listItem = document.getRoot().getChild( 1 );200 const foundElement = getSiblingListItem( listItem, {201 smallerIndent: true,202 listIndent: 1,203 direction: 'forward'204 } );205 expect( foundElement ).to.equal( document.getRoot().getChild( 4 ) );206 } );207 } );208 describe( 'getSiblingNodes()', () => {209 let editor, model, document;210 beforeEach( () => {211 return VirtualTestEditor.create( { plugins: [ ListStyleEditing ] } )212 .then( newEditor => {213 editor = newEditor;214 model = editor.model;215 document = model.document;216 } );217 } );218 afterEach( () => {219 return editor.destroy();220 } );221 it( 'should return all listItems above the current selection position (direction="backward")', () => {222 setData( model,223 '<listItem listType="bulleted" listIndent="0">0.</listItem>' +224 '<listItem listType="bulleted" listIndent="0">1.</listItem>' +225 '<listItem listType="bulleted" listIndent="0">[]2.</listItem>' +226 '<listItem listType="bulleted" listIndent="0">3.</listItem>' +227 '<listItem listType="bulleted" listIndent="0">4.</listItem>'228 );229 expect( getSiblingNodes( document.selection.getFirstPosition(), 'backward' ) ).to.deep.equal( [230 document.getRoot().getChild( 0 ),231 document.getRoot().getChild( 1 ),232 document.getRoot().getChild( 2 )233 ] );234 } );235 it( 'should return all listItems below the current selection position (direction="forward")', () => {236 setData( model,237 '<listItem listType="bulleted" listIndent="0">0.</listItem>' +238 '<listItem listType="bulleted" listIndent="0">1.</listItem>' +239 '<listItem listType="bulleted" listIndent="0">[]2.</listItem>' +240 '<listItem listType="bulleted" listIndent="0">3.</listItem>' +241 '<listItem listType="bulleted" listIndent="0">4.</listItem>'242 );243 expect( getSiblingNodes( document.selection.getFirstPosition(), 'forward' ) ).to.deep.equal( [244 document.getRoot().getChild( 3 ),245 document.getRoot().getChild( 4 )246 ] );247 } );248 it( 'should break searching when spotted a non-listItem element (direction="backward")', () => {249 setData( model,250 '<listItem listType="bulleted" listIndent="0">0.</listItem>' +251 '<listItem listType="bulleted" listIndent="0">1.</listItem>' +252 '<paragraph>Foo</paragraph>' +253 '<listItem listType="bulleted" listIndent="0">2.</listItem>' +254 '<listItem listType="bulleted" listIndent="0">3.</listItem>' +255 '<listItem listType="bulleted" listIndent="0">4.[].</listItem>'256 );257 expect( getSiblingNodes( document.selection.getFirstPosition(), 'backward' ) ).to.deep.equal( [258 document.getRoot().getChild( 3 ),259 document.getRoot().getChild( 4 ),260 document.getRoot().getChild( 5 )261 ] );262 } );263 it( 'should break searching when spotted a non-listItem element (direction="forward")', () => {264 setData( model,265 '<listItem listType="bulleted" listIndent="0">[]0.</listItem>' +266 '<listItem listType="bulleted" listIndent="0">1.</listItem>' +267 '<listItem listType="bulleted" listIndent="0">2.</listItem>' +268 '<paragraph>Foo</paragraph>' +269 '<listItem listType="bulleted" listIndent="0">3.</listItem>' +270 '<listItem listType="bulleted" listIndent="0">4.</listItem>'271 );272 expect( getSiblingNodes( document.selection.getFirstPosition(), 'forward' ) ).to.deep.equal( [273 document.getRoot().getChild( 1 ),274 document.getRoot().getChild( 2 )275 ] );276 } );277 it( 'should break searching when spotted a different value for the `listType` attribute (direction="backward")', () => {278 setData( model,279 '<listItem listType="bulleted" listIndent="0">0.</listItem>' +280 '<listItem listType="bulleted" listIndent="0">1.</listItem>' +281 '<listItem listType="bulleted" listIndent="0">2.</listItem>' +282 '<listItem listType="numbered" listIndent="0">Numbered item.</listItem>' +283 '<listItem listType="bulleted" listIndent="0">3.</listItem>' +284 '<listItem listType="bulleted" listIndent="0">[]4.</listItem>'285 );286 expect( getSiblingNodes( document.selection.getFirstPosition(), 'backward' ) ).to.deep.equal( [287 document.getRoot().getChild( 4 ),288 document.getRoot().getChild( 5 )289 ] );290 } );291 it( 'should break searching when spotted a different value for the `listType` attribute (direction="forward")', () => {292 setData( model,293 '<listItem listType="bulleted" listIndent="0">[]0.</listItem>' +294 '<listItem listType="bulleted" listIndent="0">1.</listItem>' +295 '<listItem listType="bulleted" listIndent="0">2.</listItem>' +296 '<listItem listType="numbered" listIndent="0">Numbered item.</listItem>' +297 '<listItem listType="bulleted" listIndent="0">3.</listItem>' +298 '<listItem listType="bulleted" listIndent="0">4.</listItem>'299 );300 expect( getSiblingNodes( document.selection.getFirstPosition(), 'forward' ) ).to.deep.equal( [301 document.getRoot().getChild( 1 ),302 document.getRoot().getChild( 2 )303 ] );304 } );305 it( 'should break searching when spotted a different value for the `listStyle` attribute (direction="backward")', () => {306 setData( model,307 '<listItem listType="bulleted" listStyle="disc" listIndent="0">0.</listItem>' +308 '<listItem listType="bulleted" listStyle="disc" listIndent="0">1.</listItem>' +309 '<listItem listType="bulleted" listStyle="disc" listIndent="0">2.</listItem>' +310 '<listItem listType="bulleted" listStyle="square" listIndent="0">Broken item.</listItem>' +311 '<listItem listType="bulleted" listStyle="disc" listIndent="0">3.</listItem>' +312 '<listItem listType="bulleted" listStyle="disc" listIndent="0">[]4.</listItem>'313 );314 expect( getSiblingNodes( document.selection.getFirstPosition(), 'backward' ) ).to.deep.equal( [315 document.getRoot().getChild( 4 ),316 document.getRoot().getChild( 5 )317 ] );318 } );319 it( 'should break searching when spotted a different value for the `listStyle` attribute (direction="forward")', () => {320 setData( model,321 '<listItem listType="bulleted" listStyle="disc" listIndent="0">[]0.</listItem>' +322 '<listItem listType="bulleted" listStyle="disc" listIndent="0">1.</listItem>' +323 '<listItem listType="bulleted" listStyle="disc" listIndent="0">2.</listItem>' +324 '<listItem listType="bulleted" listStyle="square" listIndent="0">Broken item.</listItem>' +325 '<listItem listType="bulleted" listStyle="disc" listIndent="0">3.</listItem>' +326 '<listItem listType="bulleted" listStyle="disc" listIndent="0">4.</listItem>'327 );328 expect( getSiblingNodes( document.selection.getFirstPosition(), 'forward' ) ).to.deep.equal( [329 document.getRoot().getChild( 1 ),330 document.getRoot().getChild( 2 )331 ] );332 } );333 it( 'should ignore nested items (looking for listIndent=0)', () => {334 setData( model,335 '<listItem listType="bulleted" listIndent="0">[]0.</listItem>' +336 '<listItem listType="bulleted" listIndent="0">1.</listItem>' +337 '<listItem listType="bulleted" listIndent="0">2.</listItem>' +338 '<listItem listType="bulleted" listIndent="1">2.1.</listItem>' +339 '<listItem listType="bulleted" listIndent="1">2.2.</listItem>' +340 '<listItem listType="bulleted" listIndent="0">3.</listItem>' +341 '<listItem listType="bulleted" listIndent="1">3.1.</listItem>' +342 '<listItem listType="bulleted" listIndent="2">3.1.1.</listItem>' +343 '<listItem listType="bulleted" listIndent="0">4.</listItem>'344 );345 expect( getSiblingNodes( document.selection.getFirstPosition(), 'forward' ) ).to.deep.equal( [346 document.getRoot().getChild( 1 ),347 document.getRoot().getChild( 2 ),348 document.getRoot().getChild( 5 ),349 document.getRoot().getChild( 8 )350 ] );351 } );352 it( 'should break when spotted an outer list (looking for listIndent=1)', () => {353 setData( model,354 '<listItem listType="bulleted" listIndent="0">0.</listItem>' +355 '<listItem listType="bulleted" listIndent="0">1.</listItem>' +356 '<listItem listType="bulleted" listIndent="1">[]1.1.</listItem>' +357 '<listItem listType="bulleted" listIndent="1">1.2.</listItem>' +358 '<listItem listType="bulleted" listIndent="1">1.3.</listItem>' +359 '<listItem listType="bulleted" listIndent="0">2.</listItem>' +360 '<listItem listType="bulleted" listIndent="0">3.</listItem>'361 );362 expect( getSiblingNodes( document.selection.getFirstPosition(), 'forward' ) ).to.deep.equal( [363 document.getRoot().getChild( 3 ),364 document.getRoot().getChild( 4 )365 ] );366 } );367 } );...
liststylecommand.js
Source:liststylecommand.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 { setData, getData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';6import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';7import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';8import ListStyleEditing from '../src/liststyleediting';9describe( 'ListStyleCommand', () => {10 let editor, model, bulletedListCommand, numberedListCommand, listStyleCommand;11 beforeEach( () => {12 return VirtualTestEditor13 .create( {14 plugins: [ Paragraph, ListStyleEditing ]15 } )16 .then( newEditor => {17 editor = newEditor;18 model = editor.model;19 bulletedListCommand = editor.commands.get( 'bulletedList' );20 numberedListCommand = editor.commands.get( 'numberedList' );21 listStyleCommand = editor.commands.get( 'listStyle' );22 } );23 } );24 afterEach( () => {25 return editor.destroy();26 } );27 describe( '#isEnabled', () => {28 it( 'should be true if bulletedList or numberedList is enabled', () => {29 bulletedListCommand.isEnabled = true;30 numberedListCommand.isEnabled = false;31 listStyleCommand.refresh();32 expect( listStyleCommand.isEnabled ).to.equal( true );33 bulletedListCommand.isEnabled = false;34 numberedListCommand.isEnabled = true;35 listStyleCommand.refresh();36 expect( listStyleCommand.isEnabled ).to.equal( true );37 } );38 it( 'should be false if bulletedList and numberedList are enabled', () => {39 bulletedListCommand.isEnabled = false;40 numberedListCommand.isEnabled = false;41 listStyleCommand.refresh();42 expect( listStyleCommand.isEnabled ).to.equal( false );43 } );44 } );45 describe( '#value', () => {46 it( 'should return null if selected a paragraph', () => {47 setData( model, '<paragraph>Foo[]</paragraph>' );48 expect( listStyleCommand.value ).to.equal( null );49 } );50 it( 'should return null if selection starts in a paragraph and ends in a list item', () => {51 setData( model,52 '<paragraph>Fo[o</paragraph>' +53 '<listItem listIndent="0" listType="bulleted" listStyle="default">Foo]</listItem>'54 );55 expect( listStyleCommand.value ).to.equal( null );56 } );57 it( 'should return the value of `listStyle` attribute if selection is inside a listItem (collapsed selection)', () => {58 setData( model, '<listItem listIndent="0" listType="bulleted" listStyle="default">Foo[]</listItem>' );59 expect( listStyleCommand.value ).to.equal( 'default' );60 } );61 it( 'should return the value of `listStyle` attribute if selection is inside a listItem (non-collapsed selection)', () => {62 setData( model, '<listItem listIndent="0" listType="bulleted" listStyle="default">[Foo]</listItem>' );63 expect( listStyleCommand.value ).to.equal( 'default' );64 } );65 it( 'should return the value of `listStyle` attribute if selected more elements in the same list', () => {66 setData( model,67 '<listItem listIndent="0" listType="bulleted" listStyle="square">[1.</listItem>' +68 '<listItem listIndent="0" listType="bulleted" listStyle="square">2.]</listItem>' +69 '<listItem listIndent="0" listType="bulleted" listStyle="square">3.</listItem>'70 );71 expect( listStyleCommand.value ).to.equal( 'square' );72 } );73 it( 'should return the value of `listStyle` attribute for the selection inside a nested list', () => {74 setData( model,75 '<listItem listIndent="0" listType="bulleted" listStyle="square">1.</listItem>' +76 '<listItem listIndent="0" listType="bulleted" listStyle="disc">1.1.[]</listItem>' +77 '<listItem listIndent="0" listType="bulleted" listStyle="square">2.</listItem>'78 );79 expect( listStyleCommand.value ).to.equal( 'disc' );80 } );81 it( 'should return the value of `listStyle` attribute from a list where the selection starts (selection over nested list)', () => {82 setData( model,83 '<listItem listIndent="0" listType="bulleted" listStyle="square">1.</listItem>' +84 '<listItem listIndent="0" listType="bulleted" listStyle="disc">1.1.[</listItem>' +85 '<listItem listIndent="0" listType="bulleted" listStyle="square">2.]</listItem>'86 );87 expect( listStyleCommand.value ).to.equal( 'disc' );88 } );89 } );90 describe( 'execute()', () => {91 it( 'should set the `listStyle` attribute for collapsed selection', () => {92 setData( model,93 '<listItem listIndent="0" listStyle="default" listType="bulleted">1.[]</listItem>'94 );95 listStyleCommand.execute( { type: 'circle' } );96 expect( getData( model ) ).to.equal(97 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.[]</listItem>'98 );99 } );100 it( 'should set the `listStyle` attribute for non-collapsed selection', () => {101 setData( model,102 '<listItem listIndent="0" listStyle="default" listType="bulleted">[1.]</listItem>'103 );104 listStyleCommand.execute( { type: 'circle' } );105 expect( getData( model ) ).to.equal(106 '<listItem listIndent="0" listStyle="circle" listType="bulleted">[1.]</listItem>'107 );108 } );109 it( 'should set the `listStyle` attribute for all the same list items (collapsed selection)', () => {110 setData( model,111 '<listItem listIndent="0" listStyle="default" listType="bulleted">1.[]</listItem>' +112 '<listItem listIndent="0" listStyle="default" listType="bulleted">2.</listItem>' +113 '<listItem listIndent="0" listStyle="default" listType="bulleted">3.</listItem>'114 );115 listStyleCommand.execute( { type: 'circle' } );116 expect( getData( model ) ).to.equal(117 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.[]</listItem>' +118 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>' +119 '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>'120 );121 } );122 it( 'should set the `listStyle` attribute for all the same list items and ignores nested lists', () => {123 setData( model,124 '<listItem listIndent="0" listStyle="default" listType="bulleted">1.[]</listItem>' +125 '<listItem listIndent="0" listStyle="default" listType="bulleted">2.</listItem>' +126 '<listItem listIndent="1" listStyle="default" listType="bulleted">2.1.</listItem>' +127 '<listItem listIndent="1" listStyle="default" listType="bulleted">2.2.</listItem>' +128 '<listItem listIndent="0" listStyle="default" listType="bulleted">3.</listItem>' +129 '<listItem listIndent="1" listStyle="default" listType="bulleted">3.1.</listItem>'130 );131 listStyleCommand.execute( { type: 'circle' } );132 expect( getData( model ) ).to.equal(133 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.[]</listItem>' +134 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>' +135 '<listItem listIndent="1" listStyle="default" listType="bulleted">2.1.</listItem>' +136 '<listItem listIndent="1" listStyle="default" listType="bulleted">2.2.</listItem>' +137 '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>' +138 '<listItem listIndent="1" listStyle="default" listType="bulleted">3.1.</listItem>'139 );140 } );141 it( 'should set the `listStyle` attribute for all the same list items and ignores "parent" list (selection in nested list)', () => {142 setData( model,143 '<listItem listIndent="0" listStyle="default" listType="bulleted">1.</listItem>' +144 '<listItem listIndent="0" listStyle="default" listType="bulleted">2.</listItem>' +145 '<listItem listIndent="1" listStyle="default" listType="bulleted">2.1.[]</listItem>' +146 '<listItem listIndent="1" listStyle="default" listType="bulleted">2.2.</listItem>' +147 '<listItem listIndent="0" listStyle="default" listType="bulleted">3.</listItem>' +148 '<listItem listIndent="1" listStyle="default" listType="bulleted">3.1.</listItem>'149 );150 listStyleCommand.execute( { type: 'disc' } );151 expect( getData( model ) ).to.equal(152 '<listItem listIndent="0" listStyle="default" listType="bulleted">1.</listItem>' +153 '<listItem listIndent="0" listStyle="default" listType="bulleted">2.</listItem>' +154 '<listItem listIndent="1" listStyle="disc" listType="bulleted">2.1.[]</listItem>' +155 '<listItem listIndent="1" listStyle="disc" listType="bulleted">2.2.</listItem>' +156 '<listItem listIndent="0" listStyle="default" listType="bulleted">3.</listItem>' +157 '<listItem listIndent="1" listStyle="default" listType="bulleted">3.1.</listItem>'158 );159 } );160 it( 'should stop searching for the list items when spotted non-listItem element', () => {161 setData( model,162 '<paragraph>Foo.</paragraph>' +163 '<listItem listIndent="0" listStyle="default" listType="bulleted">1.[]</listItem>' +164 '<listItem listIndent="0" listStyle="default" listType="bulleted">2.</listItem>' +165 '<listItem listIndent="0" listStyle="default" listType="bulleted">3.</listItem>'166 );167 listStyleCommand.execute( { type: 'circle' } );168 expect( getData( model ) ).to.equal(169 '<paragraph>Foo.</paragraph>' +170 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.[]</listItem>' +171 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>' +172 '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>'173 );174 } );175 it( 'should stop searching for the list items when spotted listItem with different listType attribute', () => {176 setData( model,177 '<paragraph>Foo.</paragraph>' +178 '<listItem listIndent="0" listStyle="default" listType="bulleted">1.[]</listItem>' +179 '<listItem listIndent="0" listStyle="default" listType="bulleted">2.</listItem>' +180 '<listItem listIndent="0" listStyle="default" listType="numbered">1.</listItem>'181 );182 listStyleCommand.execute( { type: 'circle' } );183 expect( getData( model ) ).to.equal(184 '<paragraph>Foo.</paragraph>' +185 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.[]</listItem>' +186 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>' +187 '<listItem listIndent="0" listStyle="default" listType="numbered">1.</listItem>'188 );189 } );190 it( 'should stop searching for the list items when spotted listItem with different listStyle attribute', () => {191 setData( model,192 '<paragraph>Foo.</paragraph>' +193 '<listItem listIndent="0" listStyle="default" listType="bulleted">1.[]</listItem>' +194 '<listItem listIndent="0" listStyle="default" listType="bulleted">2.</listItem>' +195 '<listItem listIndent="0" listStyle="disc" listType="bulleted">1.</listItem>'196 );197 listStyleCommand.execute( { type: 'circle' } );198 expect( getData( model ) ).to.equal(199 '<paragraph>Foo.</paragraph>' +200 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.[]</listItem>' +201 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>' +202 '<listItem listIndent="0" listStyle="disc" listType="bulleted">1.</listItem>'203 );204 } );205 it( 'should start searching for the list items from starting position (collapsed selection)', () => {206 setData( model,207 '<listItem listIndent="0" listStyle="default" listType="bulleted">1.</listItem>' +208 '<listItem listIndent="0" listStyle="default" listType="bulleted">2.</listItem>' +209 '<listItem listIndent="0" listStyle="default" listType="bulleted">[3.</listItem>' +210 '<paragraph>Foo.]</paragraph>'211 );212 listStyleCommand.execute( { type: 'circle' } );213 expect( getData( model ) ).to.equal(214 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +215 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>' +216 '<listItem listIndent="0" listStyle="circle" listType="bulleted">[3.</listItem>' +217 '<paragraph>Foo.]</paragraph>'218 );219 } );220 it( 'should start searching for the list items from ending position (collapsed selection)', () => {221 setData( model,222 '<paragraph>[Foo.</paragraph>' +223 '<listItem listIndent="0" listStyle="default" listType="bulleted">1.]</listItem>' +224 '<listItem listIndent="0" listStyle="default" listType="bulleted">2.</listItem>' +225 '<listItem listIndent="0" listStyle="default" listType="bulleted">3.</listItem>'226 );227 listStyleCommand.execute( { type: 'circle' } );228 expect( getData( model ) ).to.equal(229 '<paragraph>[Foo.</paragraph>' +230 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.]</listItem>' +231 '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>' +232 '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>'233 );234 } );235 it( 'should use default type if not specified (no options passed)', () => {236 setData( model,237 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.[]</listItem>'238 );239 listStyleCommand.execute();240 expect( getData( model ) ).to.equal(241 '<listItem listIndent="0" listStyle="default" listType="bulleted">1.[]</listItem>'242 );243 } );244 it( 'should use default type if not specified (passed an empty object)', () => {245 setData( model,246 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.[]</listItem>'247 );248 listStyleCommand.execute( {} );249 expect( getData( model ) ).to.equal(250 '<listItem listIndent="0" listStyle="default" listType="bulleted">1.[]</listItem>'251 );252 } );253 it( 'should use default type if not specified (passed null as value)', () => {254 setData( model,255 '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.[]</listItem>'256 );257 listStyleCommand.execute( { type: null } );258 expect( getData( model ) ).to.equal(259 '<listItem listIndent="0" listStyle="default" listType="bulleted">1.[]</listItem>'260 );261 } );262 it( 'should not update anything if no listItem found in the selection', () => {263 setData( model,264 '<paragraph>[Foo.]</paragraph>' +265 '<listItem listIndent="0" listStyle="default" listType="bulleted">1.</listItem>'266 );267 const modelChangeStub = sinon.stub( model, 'change' ).named( 'model#change' );268 listStyleCommand.execute( { type: 'circle' } );269 expect( getData( model ) ).to.equal(270 '<paragraph>[Foo.]</paragraph>' +271 '<listItem listIndent="0" listStyle="default" listType="bulleted">1.</listItem>'272 );273 expect( modelChangeStub.called ).to.equal( false );274 } );275 it( 'should update all items that belong to selected elements', () => {276 // [x] = items that should be updated.277 // All list items that belong to the same lists that selected items should be updated.278 // "2." is the most outer list (listIndent=0)279 // "2.1" a child list of the "2." element (listIndent=1)280 // "2.1.1" a child list of the "2.1" element (listIndent=2)281 //282 // [x] â 1.283 // [x] â [2.284 // [x] â 2.1.285 // [x] ⶠ2.1.1.]286 // [x] ⶠ2.1.2.287 // [x] â 2.2.288 // [x] â 3.289 // [ ] â 3.1.290 // [ ] ⶠ3.1.1.291 //292 // "3.1" is not selected and this list should not be updated.293 setData( model,294 '<listItem listIndent="0" listStyle="default" listType="bulleted">1.</listItem>' +295 '<listItem listIndent="0" listStyle="default" listType="bulleted">[2.</listItem>' +296 '<listItem listIndent="1" listStyle="default" listType="bulleted">2.1.</listItem>' +297 '<listItem listIndent="2" listStyle="default" listType="bulleted">2.1.1.]</listItem>' +298 '<listItem listIndent="2" listStyle="default" listType="bulleted">2.1.2.</listItem>' +299 '<listItem listIndent="1" listStyle="default" listType="bulleted">2.2.</listItem>' +300 '<listItem listIndent="0" listStyle="default" listType="bulleted">3.</listItem>' +301 '<listItem listIndent="1" listStyle="default" listType="bulleted">3.1.</listItem>' +302 '<listItem listIndent="2" listStyle="default" listType="bulleted">3.1.1.</listItem>'303 );304 listStyleCommand.execute( { type: 'disc' } );305 expect( getData( model ) ).to.equal(306 '<listItem listIndent="0" listStyle="disc" listType="bulleted">1.</listItem>' +307 '<listItem listIndent="0" listStyle="disc" listType="bulleted">[2.</listItem>' +308 '<listItem listIndent="1" listStyle="disc" listType="bulleted">2.1.</listItem>' +309 '<listItem listIndent="2" listStyle="disc" listType="bulleted">2.1.1.]</listItem>' +310 '<listItem listIndent="2" listStyle="disc" listType="bulleted">2.1.2.</listItem>' +311 '<listItem listIndent="1" listStyle="disc" listType="bulleted">2.2.</listItem>' +312 '<listItem listIndent="0" listStyle="disc" listType="bulleted">3.</listItem>' +313 '<listItem listIndent="1" listStyle="default" listType="bulleted">3.1.</listItem>' +314 '<listItem listIndent="2" listStyle="default" listType="bulleted">3.1.1.</listItem>'315 );316 } );317 } );...
SectionNavbars.js
Source:SectionNavbars.js
1import React from "react";2// @material-ui/core components3import { makeStyles } from "@material-ui/core/styles";4import List from "@material-ui/core/List";5import ListItem from "@material-ui/core/ListItem";6// @material-ui/icons7import Search from "@material-ui/icons/Search";8import Email from "@material-ui/icons/Email";9import Face from "@material-ui/icons/Face";10import Settings from "@material-ui/icons/Settings";11import AccountCircle from "@material-ui/icons/AccountCircle";12import Explore from "@material-ui/icons/Explore";13// core components14import GridContainer from "components/Grid/GridContainer.js";15import GridItem from "components/Grid/GridItem.js";16import Header from "components/Header/Header.js";17import CustomInput from "components/CustomInput/CustomInput.js";18import CustomDropdown from "components/CustomDropdown/CustomDropdown.js";19import Button from "components/CustomButtons/Button.js";20import navbarsStyle from "assets/jss/material-kit-pro-react/views/componentsSections/navbarsStyle.js";21import image from "assets/img/bg.jpg";22import profileImage from "assets/img/faces/avatar.jpg";23const useStyles = makeStyles(navbarsStyle);24export default function SectionNavbars() {25 const classes = useStyles();26 return (27 <div className={classes.section + " cd-section"} id="navigation">28 <div className={classes.container}>29 <GridContainer>30 <GridItem xs={12} sm={6} md={6}>31 <div className={classes.title}>32 <h3>Menu</h3>33 </div>34 <Header35 brand="Menu"36 color="primary"37 links={38 <List className={classes.list}>39 <ListItem className={classes.listItem}>40 <Button41 href="#pablo"42 className={classes.navLink + " " + classes.navLinkActive}43 onClick={e => e.preventDefault()}44 color="transparent"45 >46 Link47 </Button>48 </ListItem>49 <ListItem className={classes.listItem}>50 <Button51 href="#pablo"52 className={classes.navLink}53 onClick={e => e.preventDefault()}54 color="transparent"55 >56 Link57 </Button>58 </ListItem>59 <ListItem className={classes.listItem}>60 <CustomDropdown61 buttonText="Dropdown"62 dropdownHeader="Dropdown Header"63 buttonProps={{64 className: classes.navLink,65 color: "transparent"66 }}67 dropdownList={[68 "Action",69 "Another action",70 "Something else here",71 { divider: true },72 "Separated link",73 { divider: true },74 "One more separated link"75 ]}76 />77 </ListItem>78 </List>79 }80 />81 </GridItem>82 <GridItem xs={12} sm={6} md={6}>83 <div className={classes.title}>84 <h3>Menu with Icons</h3>85 </div>86 <Header87 brand="Icons"88 color="info"89 links={90 <List className={classes.list + " " + classes.mlAuto}>91 <ListItem className={classes.listItem}>92 <Button color="transparent" className={classes.navLink}>93 <Email />94 </Button>95 </ListItem>96 <ListItem className={classes.listItem}>97 <Button color="transparent" className={classes.navLink}>98 <Face />99 </Button>100 </ListItem>101 <ListItem className={classes.listItem}>102 <CustomDropdown103 left104 dropdownHeader="Dropdown Header"105 buttonIcon={Settings}106 buttonProps={{107 className: classes.navLink,108 color: "transparent"109 }}110 dropdownList={[111 "Action",112 "Another action",113 "Something else here",114 { divider: true },115 "Separated link",116 { divider: true },117 "One more separated link"118 ]}119 />120 </ListItem>121 </List>122 }123 />124 </GridItem>125 </GridContainer>126 <div className={classes.title}>127 <h3>Navigation</h3>128 </div>129 </div>130 <div id="navbar" className={classes.navbar}>131 <div132 className={classes.navigation}133 style={{ backgroundImage: "url(" + image + ")" }}134 >135 <Header136 brand="Brand"137 color="rose"138 links={139 <div className={classes.collapse}>140 <List className={classes.list + " " + classes.mrAuto}>141 <ListItem className={classes.listItem}>142 <Button143 href="#pablo"144 className={classes.navLink + " " + classes.navLinkActive}145 onClick={e => e.preventDefault()}146 color="transparent"147 >148 Link149 </Button>150 </ListItem>151 <ListItem className={classes.listItem}>152 <Button153 href="#pablo"154 className={classes.navLink}155 onClick={e => e.preventDefault()}156 color="transparent"157 >158 Link159 </Button>160 </ListItem>161 </List>162 <div className={classes.mlAuto}>163 <CustomInput164 white165 inputRootCustomClasses={classes.inputRootCustomClasses}166 formControlProps={{167 className: classes.formControl168 }}169 inputProps={{170 placeholder: "Search",171 inputProps: {172 "aria-label": "Search",173 className: classes.searchInput174 }175 }}176 />177 <Button color="white" justIcon round>178 <Search className={classes.searchIcon} />179 </Button>180 </div>181 </div>182 }183 />184 <Header185 brand="Info Color"186 color="info"187 links={188 <List className={classes.list + " " + classes.mlAuto}>189 <ListItem className={classes.listItem}>190 <Button191 href="#pablo"192 className={classes.navLink + " " + classes.navLinkActive}193 onClick={e => e.preventDefault()}194 color="transparent"195 >196 Discover197 </Button>198 </ListItem>199 <ListItem className={classes.listItem}>200 <Button201 href="#pablo"202 className={classes.navLink}203 onClick={e => e.preventDefault()}204 color="transparent"205 >206 Profile207 </Button>208 </ListItem>209 <ListItem className={classes.listItem}>210 <Button211 href="#pablo"212 className={classes.navLink}213 onClick={e => e.preventDefault()}214 color="transparent"215 >216 Settings217 </Button>218 </ListItem>219 </List>220 }221 />222 <Header223 brand="Primary Color"224 color="primary"225 links={226 <List className={classes.list + " " + classes.mlAuto}>227 <ListItem className={classes.listItem}>228 <Button229 href="#pablo"230 className={classes.navLink + " " + classes.navLinkActive}231 onClick={e => e.preventDefault()}232 color="transparent"233 >234 <Explore /> Discover235 </Button>236 </ListItem>237 <ListItem className={classes.listItem}>238 <Button239 href="#pablo"240 className={classes.navLink}241 onClick={e => e.preventDefault()}242 color="transparent"243 >244 <AccountCircle /> Profile245 </Button>246 </ListItem>247 <ListItem className={classes.listItem}>248 <Button249 href="#pablo"250 className={classes.navLink}251 onClick={e => e.preventDefault()}252 color="transparent"253 >254 <Settings /> Settings255 </Button>256 </ListItem>257 </List>258 }259 />260 <Header261 brand="Navbar with notifications"262 color="dark"263 links={264 <List className={classes.list + " " + classes.mlAuto}>265 <ListItem className={classes.listItem}>266 <Button267 href="#pablo"268 className={classes.navLink}269 onClick={e => e.preventDefault()}270 color="transparent"271 >272 Discover273 </Button>274 </ListItem>275 <ListItem className={classes.listItem}>276 <Button277 href="#pablo"278 className={classes.navLink}279 onClick={e => e.preventDefault()}280 color="transparent"281 >282 Wishlist283 </Button>284 </ListItem>285 <ListItem className={classes.listItem}>286 <Button287 href="#pablo"288 className={classes.notificationNavLink}289 onClick={e => e.preventDefault()}290 color="rose"291 justIcon292 round293 >294 <Email />295 </Button>296 </ListItem>297 <ListItem className={classes.listItem}>298 <CustomDropdown299 left300 caret={false}301 hoverColor="dark"302 dropdownHeader="Dropdown Header"303 buttonText={304 <img305 src={profileImage}306 className={classes.img}307 alt="profile"308 />309 }310 buttonProps={{311 className:312 classes.navLink + " " + classes.imageDropdownButton,313 color: "transparent"314 }}315 dropdownList={[316 "Me",317 "Settings and other stuff",318 "Sign out"319 ]}320 />321 </ListItem>322 </List>323 }324 />325 <Header326 brand="Navbar with profile"327 links={328 <List className={classes.list + " " + classes.mlAuto}>329 <ListItem className={classes.listItem}>330 <Button331 href="#pablo"332 className={classes.navLink}333 onClick={e => e.preventDefault()}334 color="transparent"335 >336 Discover337 </Button>338 </ListItem>339 <ListItem className={classes.listItem}>340 <Button341 href="#pablo"342 className={classes.navLink}343 onClick={e => e.preventDefault()}344 color="transparent"345 >346 Wishlist347 </Button>348 </ListItem>349 <ListItem className={classes.listItem}>350 <Button351 href="#pablo"352 className={classes.registerNavLink}353 onClick={e => e.preventDefault()}354 color="rose"355 round356 >357 Register358 </Button>359 </ListItem>360 </List>361 }362 />363 <Header364 brand="Transparent"365 color="transparent"366 links={367 <List className={classes.list + " " + classes.mlAuto}>368 <ListItem className={classes.listItem}>369 <Button color="transparent" className={classes.navLink}>370 <i371 className={372 classes.socialIcons +373 " " +374 classes.marginRight5 +375 " fab fa-twitter"376 }377 />{" "}378 Twitter379 </Button>380 </ListItem>381 <ListItem className={classes.listItem}>382 <Button color="transparent" className={classes.navLink}>383 <i384 className={385 classes.socialIcons +386 " " +387 classes.marginRight5 +388 " fab fa-facebook"389 }390 />{" "}391 Facebook392 </Button>393 </ListItem>394 <ListItem className={classes.listItem}>395 <Button color="transparent" className={classes.navLink}>396 <i397 className={398 classes.socialIcons +399 " " +400 classes.marginRight5 +401 " fab fa-instagram"402 }403 />{" "}404 Instagram405 </Button>406 </ListItem>407 </List>408 }409 />410 </div>411 </div>412 </div>413 );...
indentcommand.js
Source:indentcommand.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 Editor from '@ckeditor/ckeditor5-core/src/editor/editor';6import Model from '@ckeditor/ckeditor5-engine/src/model/model';7import IndentCommand from '../src/indentcommand';8import { setData, getData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';9describe( 'IndentCommand', () => {10 let editor, model, doc, root;11 beforeEach( () => {12 editor = new Editor();13 editor.model = new Model();14 model = editor.model;15 doc = model.document;16 root = doc.createRoot();17 model.schema.register( 'listItem', {18 inheritAllFrom: '$block',19 allowAttributes: [ 'listType', 'listIndent' ]20 } );21 model.schema.register( 'paragraph', { inheritAllFrom: '$block' } );22 setData(23 model,24 '<listItem listIndent="0" listType="bulleted">a</listItem>' +25 '<listItem listIndent="0" listType="bulleted">b</listItem>' +26 '<listItem listIndent="1" listType="bulleted">c</listItem>' +27 '<listItem listIndent="2" listType="bulleted">d</listItem>' +28 '<listItem listIndent="2" listType="bulleted">e</listItem>' +29 '<listItem listIndent="1" listType="bulleted">f</listItem>' +30 '<listItem listIndent="0" listType="bulleted">g</listItem>'31 );32 } );33 describe( 'IndentCommand - forward (indent)', () => {34 let command;35 beforeEach( () => {36 command = new IndentCommand( editor, 'forward' );37 } );38 afterEach( () => {39 command.destroy();40 } );41 describe( 'isEnabled', () => {42 it( 'should be true if selection starts in list item', () => {43 model.change( writer => {44 writer.setSelection( root.getChild( 5 ), 0 );45 } );46 expect( command.isEnabled ).to.be.true;47 } );48 it( 'should be false if selection starts in first list item', () => {49 model.change( writer => {50 writer.setSelection( root.getChild( 0 ), 0 );51 } );52 expect( command.isEnabled ).to.be.false;53 } );54 // Reported in PR #53.55 it( 'should be false if selection starts in first list item #2', () => {56 setData(57 model,58 '<listItem listIndent="0" listType="bulleted">a</listItem>' +59 '<listItem listIndent="1" listType="bulleted">b</listItem>' +60 '<listItem listIndent="0" listType="bulleted">c</listItem>' +61 '<listItem listIndent="1" listType="bulleted">[]d</listItem>' +62 '<listItem listIndent="2" listType="bulleted">e</listItem>'63 );64 expect( command.isEnabled ).to.be.false;65 } );66 // Reported in PR #53.67 it( 'should be false if selection starts in first list item #3', () => {68 setData(69 model,70 '<listItem listIndent="0" listType="bulleted">a</listItem>' +71 '<listItem listIndent="1" listType="bulleted">b</listItem>' +72 '<listItem listIndent="0" listType="numbered">c</listItem>' +73 '<listItem listIndent="1" listType="bulleted">d</listItem>' +74 '<listItem listIndent="0" listType="bulleted">[]e</listItem>'75 );76 expect( command.isEnabled ).to.be.false;77 } );78 it( 'should be false if selection starts in first list item of top level list with different type than previous list', () => {79 setData(80 model,81 '<listItem listIndent="0" listType="bulleted">a</listItem>' +82 '<listItem listIndent="0" listType="numbered">[]b</listItem>'83 );84 expect( command.isEnabled ).to.be.false;85 } );86 it( 'should be false if selection starts in a list item that has bigger indent than it\'s previous sibling', () => {87 model.change( writer => {88 writer.setSelection( root.getChild( 2 ), 0 );89 } );90 expect( command.isEnabled ).to.be.false;91 } );92 // Edge case but may happen that some other blocks will also use the indent attribute93 // and before we fixed it the command was enabled in such a case.94 it( 'should be false if selection starts in a paragraph with indent attribute', () => {95 model.schema.extend( 'paragraph', { allowAttributes: 'listIndent' } );96 setData( model, '<listItem listIndent="0">a</listItem><paragraph listIndent="0">b[]</paragraph>' );97 expect( command.isEnabled ).to.be.false;98 } );99 } );100 describe( 'execute()', () => {101 it( 'should use parent batch', () => {102 model.change( writer => {103 writer.setSelection( root.getChild( 5 ), 0 );104 } );105 model.change( writer => {106 expect( writer.batch.operations.length ).to.equal( 0 );107 command.execute();108 expect( writer.batch.operations.length ).to.be.above( 0 );109 } );110 } );111 it( 'should increment indent attribute by 1', () => {112 model.change( writer => {113 writer.setSelection( root.getChild( 5 ), 0 );114 } );115 command.execute();116 expect( getData( model, { withoutSelection: true } ) ).to.equal(117 '<listItem listIndent="0" listType="bulleted">a</listItem>' +118 '<listItem listIndent="0" listType="bulleted">b</listItem>' +119 '<listItem listIndent="1" listType="bulleted">c</listItem>' +120 '<listItem listIndent="2" listType="bulleted">d</listItem>' +121 '<listItem listIndent="2" listType="bulleted">e</listItem>' +122 '<listItem listIndent="2" listType="bulleted">f</listItem>' +123 '<listItem listIndent="0" listType="bulleted">g</listItem>'124 );125 } );126 it( 'should increment indent of all sub-items of indented item', () => {127 model.change( writer => {128 writer.setSelection( root.getChild( 1 ), 0 );129 } );130 command.execute();131 expect( getData( model, { withoutSelection: true } ) ).to.equal(132 '<listItem listIndent="0" listType="bulleted">a</listItem>' +133 '<listItem listIndent="1" listType="bulleted">b</listItem>' +134 '<listItem listIndent="2" listType="bulleted">c</listItem>' +135 '<listItem listIndent="3" listType="bulleted">d</listItem>' +136 '<listItem listIndent="3" listType="bulleted">e</listItem>' +137 '<listItem listIndent="2" listType="bulleted">f</listItem>' +138 '<listItem listIndent="0" listType="bulleted">g</listItem>'139 );140 } );141 it( 'should increment indent of all selected item when multiple items are selected', () => {142 model.change( writer => {143 writer.setSelection( writer.createRange(144 writer.createPositionFromPath( root.getChild( 1 ), [ 0 ] ),145 writer.createPositionFromPath( root.getChild( 3 ), [ 1 ] )146 ) );147 } );148 command.execute();149 expect( getData( model, { withoutSelection: true } ) ).to.equal(150 '<listItem listIndent="0" listType="bulleted">a</listItem>' +151 '<listItem listIndent="1" listType="bulleted">b</listItem>' +152 '<listItem listIndent="2" listType="bulleted">c</listItem>' +153 '<listItem listIndent="3" listType="bulleted">d</listItem>' +154 '<listItem listIndent="2" listType="bulleted">e</listItem>' +155 '<listItem listIndent="1" listType="bulleted">f</listItem>' +156 '<listItem listIndent="0" listType="bulleted">g</listItem>'157 );158 } );159 it( 'should fire "_executeCleanup" event after finish all operations with all changed items', done => {160 model.change( writer => {161 writer.setSelection( root.getChild( 1 ), 0 );162 } );163 command.on( '_executeCleanup', ( evt, data ) => {164 expect( data ).to.deep.equal( [165 root.getChild( 1 ),166 root.getChild( 2 ),167 root.getChild( 3 ),168 root.getChild( 4 ),169 root.getChild( 5 )170 ] );171 done();172 } );173 command.execute();174 } );175 } );176 } );177 describe( 'IndentCommand - backward (outdent)', () => {178 let command;179 beforeEach( () => {180 command = new IndentCommand( editor, 'backward' );181 } );182 afterEach( () => {183 command.destroy();184 } );185 describe( 'isEnabled', () => {186 it( 'should be true if selection starts in list item', () => {187 model.change( writer => {188 writer.setSelection( root.getChild( 5 ), 0 );189 } );190 expect( command.isEnabled ).to.be.true;191 } );192 it( 'should be true if selection starts in first list item', () => {193 // This is in contrary to forward indent command.194 model.change( writer => {195 writer.setSelection( root.getChild( 0 ), 0 );196 } );197 expect( command.isEnabled ).to.be.true;198 } );199 it( 'should be true if selection starts in a list item that has bigger indent than it\'s previous sibling', () => {200 // This is in contrary to forward indent command.201 model.change( writer => {202 writer.setSelection( root.getChild( 2 ), 0 );203 } );204 expect( command.isEnabled ).to.be.true;205 } );206 } );207 describe( 'execute()', () => {208 it( 'should decrement indent attribute by 1 (if it is bigger than 0)', () => {209 model.change( writer => {210 writer.setSelection( root.getChild( 5 ), 0 );211 } );212 command.execute();213 expect( getData( model, { withoutSelection: true } ) ).to.equal(214 '<listItem listIndent="0" listType="bulleted">a</listItem>' +215 '<listItem listIndent="0" listType="bulleted">b</listItem>' +216 '<listItem listIndent="1" listType="bulleted">c</listItem>' +217 '<listItem listIndent="2" listType="bulleted">d</listItem>' +218 '<listItem listIndent="2" listType="bulleted">e</listItem>' +219 '<listItem listIndent="0" listType="bulleted">f</listItem>' +220 '<listItem listIndent="0" listType="bulleted">g</listItem>'221 );222 } );223 it( 'should rename listItem to paragraph (if indent is equal to 0)', () => {224 model.change( writer => {225 writer.setSelection( root.getChild( 0 ), 0 );226 } );227 command.execute();228 expect( getData( model, { withoutSelection: true } ) ).to.equal(229 '<paragraph listIndent="0" listType="bulleted">a</paragraph>' +230 '<listItem listIndent="0" listType="bulleted">b</listItem>' +231 '<listItem listIndent="1" listType="bulleted">c</listItem>' +232 '<listItem listIndent="2" listType="bulleted">d</listItem>' +233 '<listItem listIndent="2" listType="bulleted">e</listItem>' +234 '<listItem listIndent="1" listType="bulleted">f</listItem>' +235 '<listItem listIndent="0" listType="bulleted">g</listItem>'236 );237 } );238 it( 'should decrement indent of all sub-items of outdented item', () => {239 model.change( writer => {240 writer.setSelection( root.getChild( 1 ), 0 );241 } );242 command.execute();243 expect( getData( model, { withoutSelection: true } ) ).to.equal(244 '<listItem listIndent="0" listType="bulleted">a</listItem>' +245 '<paragraph listIndent="0" listType="bulleted">b</paragraph>' +246 '<listItem listIndent="0" listType="bulleted">c</listItem>' +247 '<listItem listIndent="1" listType="bulleted">d</listItem>' +248 '<listItem listIndent="1" listType="bulleted">e</listItem>' +249 '<listItem listIndent="0" listType="bulleted">f</listItem>' +250 '<listItem listIndent="0" listType="bulleted">g</listItem>'251 );252 } );253 it( 'should outdent all selected item when multiple items are selected', () => {254 model.change( writer => {255 writer.setSelection( writer.createRange(256 writer.createPositionFromPath( root.getChild( 1 ), [ 0 ] ),257 writer.createPositionFromPath( root.getChild( 3 ), [ 1 ] )258 ) );259 } );260 command.execute();261 expect( getData( model, { withoutSelection: true } ) ).to.equal(262 '<listItem listIndent="0" listType="bulleted">a</listItem>' +263 '<paragraph listIndent="0" listType="bulleted">b</paragraph>' +264 '<listItem listIndent="0" listType="bulleted">c</listItem>' +265 '<listItem listIndent="1" listType="bulleted">d</listItem>' +266 '<listItem listIndent="2" listType="bulleted">e</listItem>' +267 '<listItem listIndent="1" listType="bulleted">f</listItem>' +268 '<listItem listIndent="0" listType="bulleted">g</listItem>'269 );270 } );271 } );272 } );...
todolistcheckcommand.js
Source:todolistcheckcommand.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 TodoListEditing from '../src/todolistediting';6import TodoListCheckCommand from '../src/todolistcheckcommand';7import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor';8import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';9describe( 'TodoListCheckCommand', () => {10 let editor, model, command;11 beforeEach( () => {12 return ModelTestEditor13 .create( {14 plugins: [ TodoListEditing ]15 } )16 .then( newEditor => {17 editor = newEditor;18 model = editor.model;19 command = new TodoListCheckCommand( editor );20 } );21 } );22 describe( 'value', () => {23 it( 'should be false when selection is in not checked element', () => {24 setModelData( model, '<listItem listIndent="0" listType="todo">ab[]c</listItem>' );25 expect( command.value ).to.equal( false );26 } );27 it( 'should be true when selection is in checked element', () => {28 setModelData( model, '<listItem listIndent="0" listType="todo" todoListChecked="true">ab[]c</listItem>' );29 expect( command.value ).to.equal( true );30 } );31 it( 'should be false when at least one selected element is not checked', () => {32 setModelData( model,33 '<listItem listIndent="0" listType="todo" todoListChecked="true">ab[c</listItem>' +34 '<paragraph>abc</paragraph>' +35 '<listItem listIndent="0" listType="todo">abc</listItem>' +36 '<listItem listIndent="0" listType="todo" todoListChecked="true">ab]c</listItem>'37 );38 expect( command.value ).to.equal( false );39 } );40 it( 'should be true when all selected elements are checked', () => {41 setModelData( model,42 '<listItem listIndent="0" listType="todo" todoListChecked="true">ab[c</listItem>' +43 '<paragraph>abc</paragraph>' +44 '<listItem listIndent="0" listType="todo" todoListChecked="true">abc</listItem>' +45 '<listItem listIndent="0" listType="todo" todoListChecked="true">ab]c</listItem>'46 );47 expect( command.value ).to.equal( true );48 } );49 } );50 describe( 'isEnabled', () => {51 it( 'should be enabled when selection is inside to-do list item', () => {52 setModelData( model, '<listItem listIndent="0" listType="todo">a[b]c</listItem>' );53 expect( command.isEnabled ).to.equal( true );54 } );55 it( 'should be disabled when selection is not inside to-do list item', () => {56 setModelData( model, '<paragraph>a[b]c</paragraph>' );57 expect( command.isEnabled ).to.equal( false );58 } );59 it( 'should be enabled when at least one to-do list item is selected', () => {60 setModelData( model,61 '<paragraph>a[bc</paragraph>' +62 '<listItem listIndent="0" listType="todo">abc</listItem>' +63 '<paragraph>ab]c</paragraph>'64 );65 expect( command.isEnabled ).to.equal( true );66 } );67 it( 'should be enabled when none to-do list item is selected', () => {68 setModelData( model,69 '<paragraph>a[bc</paragraph>' +70 '<paragraph>abc</paragraph>' +71 '<paragraph>a]bc</paragraph>'72 );73 expect( command.isEnabled ).to.equal( false );74 } );75 } );76 describe( 'execute()', () => {77 it( 'should toggle checked state on to-do list item when collapsed selection is inside this item', () => {78 setModelData( model, '<listItem listIndent="0" listType="todo">b[]ar</listItem>' );79 command.execute();80 expect( getModelData( model ) ).to.equal(81 '<listItem listIndent="0" listType="todo" todoListChecked="true">b[]ar</listItem>'82 );83 command.execute();84 expect( getModelData( model ) ).to.equal(85 '<listItem listIndent="0" listType="todo">b[]ar</listItem>'86 );87 } );88 it( 'should toggle checked state on to-do list item when non-collapsed selection is inside this item', () => {89 setModelData( model, '<listItem listIndent="0" listType="todo">b[a]r</listItem>' );90 command.execute();91 expect( getModelData( model ) ).to.equal(92 '<listItem listIndent="0" listType="todo" todoListChecked="true">b[a]r</listItem>'93 );94 command.execute();95 expect( getModelData( model ) ).to.equal(96 '<listItem listIndent="0" listType="todo">b[a]r</listItem>'97 );98 } );99 it( 'should toggle state on multiple items', () => {100 setModelData( model,101 '<listItem listIndent="0" listType="todo">abc[</listItem>' +102 '<listItem listIndent="0" listType="todo">def</listItem>' +103 '<listItem listIndent="0" listType="todo">]ghi</listItem>'104 );105 command.execute();106 expect( getModelData( model ) ).to.equal(107 '<listItem listIndent="0" listType="todo" todoListChecked="true">abc[</listItem>' +108 '<listItem listIndent="0" listType="todo" todoListChecked="true">def</listItem>' +109 '<listItem listIndent="0" listType="todo" todoListChecked="true">]ghi</listItem>'110 );111 command.execute();112 expect( getModelData( model ) ).to.equal(113 '<listItem listIndent="0" listType="todo">abc[</listItem>' +114 '<listItem listIndent="0" listType="todo">def</listItem>' +115 '<listItem listIndent="0" listType="todo">]ghi</listItem>'116 );117 } );118 it( 'should toggle state on multiple items mixed with none to-do list items', () => {119 setModelData( model,120 '<paragraph>a[bc</paragraph>' +121 '<listItem listIndent="0" listType="todo">def</listItem>' +122 '<listItem listIndent="0" listType="numbered">ghi</listItem>' +123 '<listItem listIndent="0" listType="todo">jkl</listItem>' +124 '<paragraph>mn]o</paragraph>'125 );126 command.execute();127 expect( getModelData( model ) ).to.equal(128 '<paragraph>a[bc</paragraph>' +129 '<listItem listIndent="0" listType="todo" todoListChecked="true">def</listItem>' +130 '<listItem listIndent="0" listType="numbered">ghi</listItem>' +131 '<listItem listIndent="0" listType="todo" todoListChecked="true">jkl</listItem>' +132 '<paragraph>mn]o</paragraph>'133 );134 command.execute();135 expect( getModelData( model ) ).to.equal(136 '<paragraph>a[bc</paragraph>' +137 '<listItem listIndent="0" listType="todo">def</listItem>' +138 '<listItem listIndent="0" listType="numbered">ghi</listItem>' +139 '<listItem listIndent="0" listType="todo">jkl</listItem>' +140 '<paragraph>mn]o</paragraph>'141 );142 } );143 it( 'should mark all selected items as checked when at least one selected item is not checked', () => {144 setModelData( model,145 '<listItem listIndent="0" listType="todo">abc[</listItem>' +146 '<listItem listIndent="0" listType="todo" todoListChecked="true">def</listItem>' +147 '<listItem listIndent="0" listType="todo">]ghi</listItem>'148 );149 command.execute();150 expect( getModelData( model ) ).to.equal(151 '<listItem listIndent="0" listType="todo" todoListChecked="true">abc[</listItem>' +152 '<listItem listIndent="0" listType="todo" todoListChecked="true">def</listItem>' +153 '<listItem listIndent="0" listType="todo" todoListChecked="true">]ghi</listItem>'154 );155 } );156 it( 'should do nothing when there is no elements to toggle attribute', () => {157 setModelData( model, '<paragraph>b[]ar</paragraph>' );158 command.execute();159 expect( getModelData( model ) ).to.equal( '<paragraph>b[]ar</paragraph>' );160 } );161 it( 'should be up to date just before execution', () => {162 setModelData( model,163 '<listItem listIndent="0" listType="0">f[]oo</listItem>' +164 '<listItem listIndent="0" listType="0">bar</listItem>'165 );166 model.change( writer => {167 writer.setSelection( model.document.getRoot().getChild( 1 ), 'end' );168 command.execute();169 } );170 } );171 it( 'should set attribute if `forceValue` parameter is set to `true`', () => {172 setModelData( model, '<listItem listIndent="0" listType="todo" todoListChecked="true">b[]ar</listItem>' );173 command.execute( { forceValue: true } );174 expect( getModelData( model ) ).to.equal(175 '<listItem listIndent="0" listType="todo" todoListChecked="true">b[]ar</listItem>'176 );177 } );178 it( 'should remove attribute if `forceValue` parameter is set to `false`', () => {179 setModelData( model, '<listItem listIndent="0" listType="todo">b[]ar</listItem>' );180 command.execute( { forceValue: false } );181 expect( getModelData( model ) ).to.equal(182 '<listItem listIndent="0" listType="todo">b[]ar</listItem>'183 );184 } );185 } );...
Using AI Code Generation
1const { openBrowser, goto, listItem, closeBrowser } = require('taiko');2(async () => {3 try {4 await openBrowser();5 await goto("google.com");6 await listItem({class:"gb_P"}).exists();7 await listItem({class:"gb_P"}).click();8 await listItem({class:"gb_P"}).isDisabled();9 await listItem({class:"gb_P"}).isHidden();10 await listItem({class:"gb_P"}).isVisible();11 await listItem({class:"gb_P"}).text();12 await listItem({class:"gb_P"}).value();13 await listItem({class:"gb_P"}).select();14 await listItem({class:"gb_P"}).deselect();15 await listItem({class:"gb_P"}).hover();16 await listItem({class:"gb_P"}).rightClick();17 await listItem({class:"gb_P"}).doubleClick();18 await listItem({class:"gb_P"}).write('text');19 await listItem({class:"gb_P"}).clear();20 await listItem({class:"gb_P"}).press('Enter');21 await listItem({class:"gb_P"}).highlight();22 await listItem({class:"gb_P"}).scrollTo();23 await listItem({class:"gb_P"}).dragAndDrop(listItem({class:"gb_P"}));24 await listItem({class:"gb_P"}).element();25 await listItem({class:"gb_P"}).elements();26 await listItem({class:"gb_P"}).evaluate(() => {});27 } catch (e) {28 console.error(e);29 } finally {30 await closeBrowser();31 }32})();
Using AI Code Generation
1const taiko = require('taiko');2(async () => {3 try {4 await taiko.openBrowser();5 await taiko.write("taiko");6 await taiko.press("Enter");7 await taiko.click(taiko.link("taiko - Google Search"));8 await taiko.click(taiko.listItem("Get Started"));9 await taiko.closeBrowser();10 } catch (error) {11 console.error(error);12 }13})();14const taiko = require('taiko');15(async () => {16 try {17 await taiko.openBrowser();18 await taiko.write("taiko");19 await taiko.press("Enter");20 await taiko.click(taiko
Using AI Code Generation
1const { listItem } = require('taiko');2const { click } = require('taiko');3(async () => {4 try {5 await openBrowser({ headless: false });6 await listItem("Images").exists();7 await click(listItem("Images"));8 } catch (error) {9 console.error(error);10 } finally {11 await closeBrowser();12 }13})();14const { link } = require('taiko');15const { click } = require('taiko');16(async () => {17 try {18 await openBrowser({ headless: false });19 await link("Images").exists();20 await click(link("Images"));21 } catch (error) {22 console.error(error);23 } finally {24 await closeBrowser();25 }26})();27const { textBox } = require('taiko');28const { write } = require('taiko');29const { press } = require('taiko');30(async () => {31 try {32 await openBrowser({ headless: false });33 await textBox("Search").exists();34 await write("images", textBox("Search"));35 await press("Enter");36 }
Using AI Code Generation
1const { listItem } = require('taiko');2const { click } = require('taiko');3const { openBrowser, goto, closeBrowser } = require('taiko');4const { write } = require('taiko');5const { dropDown } = require('taiko');6const { textBox } = require('taiko');7const { text } = require('taiko');8const { image } = require('taiko');9const { link } = require('taiko');10const { button } = require('taiko');11const { checkBox } = require('taiko');12const { radioButton } = require('taiko');13const { toRightOf } = require('taiko');14const { toLeftOf } = require('taiko');15const { below } = require('taiko');16const { above } = require('taiko');17const { near } = require('taiko');18const { in: searchIn } = require('taiko');19const { near: searchNear } = require('taiko');20const { to } = require('taiko');21const { into } = require('taiko');22const { focus } = require('taiko');23const { scrollDown } = require('taiko');24const { scrollUp } = require('taiko');
Using AI Code Generation
1const { listItem } = require('taiko');2listItem('item').exists();3listItem({ id: 'item' }).exists();4listItem({ class: 'item' }).exists();5listItem({ index: 0 }).exists();6listItem({ text: 'item' }).exists();7listItem({ class: 'item', index: 0 }).exists();8listItem({ class: 'item', text: 'item' }).exists();9listItem({ id: 'item', index: 0 }).exists();10listItem({ id: 'item', text: 'item' }).exists();11listItem({ id: 'item', class: 'item' }).exists();12listItem({ id: 'item', class: 'item', index: 0 }).exists();13listItem({ id: 'item', class: 'item', text: 'item' }).exists();14const { table } = require('taiko');15table('table').exists();16table({ id: 'table' }).exists();17table({ class: 'table' }).exists();18table({ index: 0 }).exists();19table({ text: 'table' }).exists();20table({ class: 'table', index: 0 }).exists();21table({ class: 'table', text: 'table' }).exists();22table({ id: 'table', index: 0 }).exists();23table({ id: 'table', text: 'table' }).exists();24table({ id: 'table', class: 'table' }).exists();25table({ id: 'table', class: 'table', index: 0 }).exists();26table({ id: 'table', class: 'table', text: 'table' }).exists();27const { tab } = require('taiko');28tab('tab').exists();29tab({ id: 'tab' }).exists();30tab({ class: 'tab' }).exists();31tab({ index: 0 }).exists();32tab({ text: 'tab' }).exists();33tab({ class: 'tab', index: 0 }).exists();34tab({ class: 'tab', text: 'tab' }).exists();35tab({ id: 'tab', index: 0 }).exists();36tab({ id: 'tab', text: 'tab' }).exists();37tab({ id: 'tab',
Using AI Code Generation
1const { listItem, openBrowser, goto, write, closeBrowser } = require('taiko');2(async () => {3 try {4 await openBrowser({ headless: false });5 await write("Taiko");6 await listItem("TaikoJS").click();7 } catch (error) {8 console.error(error);9 } finally {10 await closeBrowser();11 }12})();13const { openBrowser, goto, closeBrowser } = require('taiko');14(async () => {15 try {16 await openBrowser({ headless: false });17 } catch (error) {18 console.error(error);19 } finally {20 await closeBrowser();21 }22})();23const { openBrowser, goto, press, closeBrowser } = require('taiko');24(async () => {25 try {26 await openBrowser({ headless: false });27 await press('Enter');28 } catch (error) {29 console.error(error);30 } finally {31 await closeBrowser();32 }33})();34const { openBrowser, goto, reload, closeBrowser } = require('taiko');35(async () => {36 try {37 await openBrowser({ headless: false });38 await reload();39 } catch (error) {40 console.error(error);41 } finally {42 await closeBrowser();43 }44})();45const { openBrowser, goto, scrollTo, closeBrowser } = require('
Using AI Code Generation
1const { listItem } = require('taiko');2(async () => {3 try {4 await openBrowser();5 await click('Start');6 await waitFor('Hello World!');7 let text = await listItem('Hello World!').text();8 console.log(text);9 await closeBrowser();10 } catch (error) {11 console.error(error);12 }13})();14const { link } = require('taiko');15(async () => {16 try {17 await openBrowser();18 await link('Shifting Content').click();19 await closeBrowser();20 } catch (error) {21 console.error(error);22 }23})();24const { button } = require('taiko');25(async () => {26 try {27 await openBrowser();28 await button('Remove').click();29 await closeBrowser();30 } catch (error) {31 console.error(error);32 }33})();34const { button } = require('taiko');35(async () => {36 try {37 await openBrowser();38 await button('Remove').click();39 await closeBrowser();40 } catch (error) {41 console.error(error);42 }43})();
Using AI Code Generation
1const { listItem } = require('taiko');2const assert = require("assert");3const test_name = "listItem";4step("Test listItem <name>", async function(name) {5await listItem(name).exists();6});7step("Test listItem <name> with text <text>", async function(name, text) {8await listItem(name).exists();9assert.equal(await listItem(name).text(), text);10});11step("Test listItem <name> with value <value>", async function(name, value) {12await listItem(name).exists();13assert.equal(await listItem(name).value(), value);14});15step("Test listItem <name> with index <index>", async function(name, index) {16await listItem(name).exists();17assert.equal(await listItem(name).index(), index);18});19step("Test listItem <name> with description <description>", async function(name, description) {20await listItem(name).exists();21assert.equal(await listItem(name).description(), description);22});23step("Test listItem <name> with selected <selected>", async function(name, selected) {24await listItem(name).exists();25assert.equal(await listItem(name).selected(), selected);26});27step("Test listItem <name> with visible <visible>", async function(name, visible) {28await listItem(name).exists();29assert.equal(await listItem(name).visible(), visible);30});31step("Test listItem <name> with enabled <enabled>", async function(name, enabled) {32await listItem(name).exists();33assert.equal(await listItem(name).enabled(), enabled);34});35step("Test listItem <name> with class <class>", async function(name, class) {36await listItem(name).exists();37assert.equal(await listItem(name).class(), class);38});39step("Test listItem <name> with id <id>", async function(name, id) {40await listItem(name).exists();41assert.equal(await listItem(name).id(), id);42});43step("Test listItem <name> with name <name>", async function(name, name) {44await listItem(name).exists();45assert.equal(await listItem(name).name(), name);46});47step("Test listItem <name> with type <type>", async function(name, type) {48await listItem(name).exists();49assert.equal(await listItem(name).type(), type);50});51step("Test listItem <name> with value <value>", async function(name, value) {52await listItem(name).exists();53assert.equal(await listItem(name).value(), value);54});55step("Test listItem <name> with text <text>", async function(name, text) {56await listItem(name
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!