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(taikoUsing 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(nameLearn 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!!
