liststyleediting.js 58 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426
  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-license
  4. */
  5. import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
  6. import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
  7. import Typing from '@ckeditor/ckeditor5-typing/src/typing';
  8. import UndoEditing from '@ckeditor/ckeditor5-undo/src/undoediting';
  9. import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
  10. import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
  11. import ListStyleEditing from '../src/liststyleediting';
  12. import TodoListEditing from '../src/todolistediting';
  13. import ListStyleCommand from '../src/liststylecommand';
  14. describe( 'ListStyleEditing', () => {
  15. let editor, model, view;
  16. beforeEach( () => {
  17. return VirtualTestEditor
  18. .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. } );
  286. } );
  287. // At this moment editing and data pipelines produce exactly the same content.
  288. // Just a few tests will be enough here. `model to data` block contains all cases checked.
  289. describe( 'conversion in editing pipeline', () => {
  290. describe( 'model to view', () => {
  291. it( 'should convert single list (type: bulleted, style: default)', () => {
  292. setModelData( model,
  293. '<listItem listIndent="0" listType="bulleted" listStyle="default">Foo</listItem>' +
  294. '<listItem listIndent="0" listType="bulleted" listStyle="default">Bar</listItem>'
  295. );
  296. expect( getViewData( view, { withoutSelection: true } ) ).to.equal(
  297. '<ul><li>Foo</li><li>Bar</li></ul>'
  298. );
  299. } );
  300. it( 'should convert single list (type: bulleted, style: circle)', () => {
  301. setModelData( model,
  302. '<listItem listIndent="0" listType="bulleted" listStyle="circle">Foo</listItem>' +
  303. '<listItem listIndent="0" listType="bulleted" listStyle="circle">Bar</listItem>'
  304. );
  305. expect( getViewData( view, { withoutSelection: true } ) ).to.equal(
  306. '<ul style="list-style-type:circle"><li>Foo</li><li>Bar</li></ul>'
  307. );
  308. } );
  309. it( 'should convert nested bulleted lists (main: circle, nested: disc)', () => {
  310. setModelData( model,
  311. '<listItem listIndent="0" listType="bulleted" listStyle="circle">Foo 1</listItem>' +
  312. '<listItem listIndent="1" listType="bulleted" listStyle="disc">Bar 1</listItem>' +
  313. '<listItem listIndent="1" listType="bulleted" listStyle="disc">Bar 2</listItem>' +
  314. '<listItem listIndent="0" listType="bulleted" listStyle="circle">Foo 2</listItem>' +
  315. '<listItem listIndent="0" listType="bulleted" listStyle="circle">Foo 3</listItem>'
  316. );
  317. expect( getViewData( view, { withoutSelection: true } ) ).to.equal(
  318. '<ul style="list-style-type:circle">' +
  319. '<li>Foo 1' +
  320. '<ul style="list-style-type:disc">' +
  321. '<li>Bar 1</li>' +
  322. '<li>Bar 2</li>' +
  323. '</ul>' +
  324. '</li>' +
  325. '<li>Foo 2</li>' +
  326. '<li>Foo 3</li>' +
  327. '</ul>'
  328. );
  329. } );
  330. // See: #8081.
  331. it( 'should convert properly nested list styles', () => {
  332. // ■ Level 0
  333. // ▶ Level 0.1
  334. // ○ Level 0.1.1
  335. // ▶ Level 0.2
  336. // ○ Level 0.2.1
  337. setModelData( model,
  338. '<listItem listIndent="0" listType="bulleted" listStyle="default">Level 0</listItem>' +
  339. '<listItem listIndent="1" listType="bulleted" listStyle="default">Level 0.1</listItem>' +
  340. '<listItem listIndent="2" listType="bulleted" listStyle="circle">Level 0.1.1</listItem>' +
  341. '<listItem listIndent="1" listType="bulleted" listStyle="default">Level 0.2</listItem>' +
  342. '<listItem listIndent="2" listType="bulleted" listStyle="circle">Level 0.2.1</listItem>'
  343. );
  344. expect( getViewData( view, { withoutSelection: true } ) ).to.equal(
  345. '<ul>' +
  346. '<li>Level 0' +
  347. '<ul>' +
  348. '<li>Level 0.1' +
  349. '<ul style="list-style-type:circle">' +
  350. '<li>Level 0.1.1</li>' +
  351. '</ul>' +
  352. '</li>' +
  353. '<li>Level 0.2' +
  354. '<ul style="list-style-type:circle">' +
  355. '<li>Level 0.2.1</li>' +
  356. '</ul>' +
  357. '</li>' +
  358. '</ul>' +
  359. '</li>' +
  360. '</ul>'
  361. );
  362. } );
  363. } );
  364. } );
  365. describe( 'integrations', () => {
  366. describe( 'merging a list into a styled list', () => {
  367. it( 'should inherit the list style attribute when merging the same kind of lists (from top, merge a single item)', () => {
  368. setModelData( model,
  369. '<paragraph>Foo Bar.[]</paragraph>' +
  370. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
  371. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
  372. );
  373. editor.execute( 'bulletedList' );
  374. expect( getModelData( model ) ).to.equal(
  375. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo Bar.[]</listItem>' +
  376. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
  377. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
  378. );
  379. } );
  380. it( 'should inherit the list style attribute when merging the same kind of lists (from top, merge a few items)', () => {
  381. setModelData( model,
  382. '<paragraph>[Foo Bar 1.</paragraph>' +
  383. '<paragraph>Foo Bar 2.]</paragraph>' +
  384. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
  385. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
  386. );
  387. editor.execute( 'bulletedList' );
  388. expect( getModelData( model ) ).to.equal(
  389. '<listItem listIndent="0" listStyle="circle" listType="bulleted">[Foo Bar 1.</listItem>' +
  390. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo Bar 2.]</listItem>' +
  391. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
  392. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
  393. );
  394. } );
  395. it( 'should not inherit anything if there is no list below the inserted list', () => {
  396. setModelData( model,
  397. '<paragraph>Foo Bar 1.[]</paragraph>' +
  398. '<paragraph>Foo Bar 2.</paragraph>'
  399. );
  400. editor.execute( 'bulletedList' );
  401. expect( getModelData( model ) ).to.equal(
  402. '<listItem listIndent="0" listStyle="default" listType="bulleted">Foo Bar 1.[]</listItem>' +
  403. '<paragraph>Foo Bar 2.</paragraph>'
  404. );
  405. } );
  406. it( 'should not inherit anything if replacing the entire content with a list', () => {
  407. setModelData( model,
  408. '<paragraph>Foo Bar 1.[]</paragraph>'
  409. );
  410. editor.execute( 'bulletedList' );
  411. expect( getModelData( model ) ).to.equal(
  412. '<listItem listIndent="0" listStyle="default" listType="bulleted">Foo Bar 1.[]</listItem>'
  413. );
  414. } );
  415. it( 'should not inherit the list style attribute when merging different kind of lists (from top, merge a single item)', () => {
  416. setModelData( model,
  417. '<paragraph>Foo Bar.[]</paragraph>' +
  418. '<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Foo</listItem>' +
  419. '<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Bar</listItem>'
  420. );
  421. editor.execute( 'bulletedList' );
  422. expect( getModelData( model ) ).to.equal(
  423. '<listItem listIndent="0" listStyle="default" listType="bulleted">Foo Bar.[]</listItem>' +
  424. '<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Foo</listItem>' +
  425. '<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Bar</listItem>'
  426. );
  427. } );
  428. it(
  429. 'should not inherit the list style attribute when merging different kind of lists (from bottom, merge a single item)',
  430. () => {
  431. setModelData( model,
  432. '<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Foo</listItem>' +
  433. '<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Bar</listItem>' +
  434. '<paragraph>Foo Bar.[]</paragraph>'
  435. );
  436. editor.execute( 'bulletedList' );
  437. expect( getModelData( model ) ).to.equal(
  438. '<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Foo</listItem>' +
  439. '<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Bar</listItem>' +
  440. '<listItem listIndent="0" listStyle="default" listType="bulleted">Foo Bar.[]</listItem>'
  441. );
  442. }
  443. );
  444. it( 'should inherit the list style attribute when merging the same kind of lists (from bottom, merge a single item)', () => {
  445. setModelData( model,
  446. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
  447. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>' +
  448. '<paragraph>Foo Bar.[]</paragraph>'
  449. );
  450. editor.execute( 'bulletedList' );
  451. expect( getModelData( model ) ).to.equal(
  452. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
  453. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>' +
  454. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo Bar.[]</listItem>'
  455. );
  456. } );
  457. it(
  458. 'should inherit the list style attribute from listIndent=0 element when merging the same kind of lists (from bottom)',
  459. () => {
  460. setModelData( model,
  461. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
  462. '<listItem listIndent="1" listStyle="square" listType="bulleted">Bar</listItem>' +
  463. '<listItem listIndent="2" listStyle="disc" listType="bulleted">Foo Bar</listItem>' +
  464. '<paragraph>Foo Bar.[]</paragraph>'
  465. );
  466. editor.execute( 'bulletedList' );
  467. expect( getModelData( model ) ).to.equal(
  468. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
  469. '<listItem listIndent="1" listStyle="square" listType="bulleted">Bar</listItem>' +
  470. '<listItem listIndent="2" listStyle="disc" listType="bulleted">Foo Bar</listItem>' +
  471. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo Bar.[]</listItem>'
  472. );
  473. }
  474. );
  475. } );
  476. describe( 'modifying "listType" attribute', () => {
  477. it( 'should inherit the list style attribute when the modified list is the same kind of the list as next sibling', () => {
  478. setModelData( model,
  479. '<listItem listIndent="0" listStyle="default" listType="numbered">Foo Bar.[]</listItem>' +
  480. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
  481. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
  482. );
  483. editor.execute( 'bulletedList' );
  484. expect( getModelData( model ) ).to.equal(
  485. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo Bar.[]</listItem>' +
  486. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
  487. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
  488. );
  489. } );
  490. it( 'should inherit the list style attribute when the modified list is the same kind of the list as previous sibling', () => {
  491. setModelData( model,
  492. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
  493. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>' +
  494. '<listItem listIndent="0" listStyle="default" listType="numbered">Foo Bar.[]</listItem>'
  495. );
  496. editor.execute( 'bulletedList' );
  497. expect( getModelData( model ) ).to.equal(
  498. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
  499. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>' +
  500. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo Bar.[]</listItem>'
  501. );
  502. } );
  503. it( 'should not inherit the list style attribute when the modified list already has defined it (next sibling check)', () => {
  504. setModelData( model,
  505. '<listItem listIndent="0" listStyle="default" listType="bulleted">Foo Bar.[]</listItem>' +
  506. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
  507. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
  508. );
  509. editor.execute( 'listStyle', { type: 'disc' } );
  510. expect( getModelData( model ) ).to.equal(
  511. '<listItem listIndent="0" listStyle="disc" listType="bulleted">Foo Bar.[]</listItem>' +
  512. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
  513. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
  514. );
  515. } );
  516. it(
  517. 'should not inherit the list style attribute when the modified list already has defined it (previous sibling check)',
  518. () => {
  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="bulleted">Foo Bar.[]</listItem>'
  523. );
  524. editor.execute( 'listStyle', { type: 'disc' } );
  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="disc" listType="bulleted">Foo Bar.[]</listItem>'
  529. );
  530. }
  531. );
  532. } );
  533. describe( 'indenting lists', () => {
  534. it( 'should restore the default value for the list style attribute when indenting a single item', () => {
  535. setModelData( model,
  536. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  537. '<listItem listIndent="1" listStyle="default" listType="bulleted">1A.</listItem>' +
  538. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2B.</listItem>' +
  539. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.[]</listItem>' +
  540. '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>'
  541. );
  542. editor.execute( 'indentList' );
  543. expect( getModelData( model ) ).to.equal(
  544. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  545. '<listItem listIndent="1" listStyle="default" listType="bulleted">1A.</listItem>' +
  546. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2B.</listItem>' +
  547. '<listItem listIndent="1" listStyle="default" listType="bulleted">2.[]</listItem>' +
  548. '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>'
  549. );
  550. } );
  551. it( 'should restore the default value for the list style attribute when indenting a few items', () => {
  552. setModelData( model,
  553. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  554. '<listItem listIndent="0" listStyle="circle" listType="bulleted">[2.</listItem>' +
  555. '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.]</listItem>'
  556. );
  557. editor.execute( 'indentList' );
  558. expect( getModelData( model ) ).to.equal(
  559. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  560. '<listItem listIndent="1" listStyle="default" listType="bulleted">[2.</listItem>' +
  561. '<listItem listIndent="1" listStyle="default" listType="bulleted">3.]</listItem>'
  562. );
  563. } );
  564. it(
  565. 'should copy the value for the list style attribute when indenting a single item into a nested list (default value)',
  566. () => {
  567. setModelData( model,
  568. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  569. '<listItem listIndent="1" listStyle="default" listType="bulleted">2.</listItem>' +
  570. '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.[]</listItem>' +
  571. '<listItem listIndent="0" listStyle="circle" listType="bulleted">4.</listItem>'
  572. );
  573. editor.execute( 'indentList' );
  574. expect( getModelData( model ) ).to.equal(
  575. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  576. '<listItem listIndent="1" listStyle="default" listType="bulleted">2.</listItem>' +
  577. '<listItem listIndent="1" listStyle="default" listType="bulleted">3.[]</listItem>' +
  578. '<listItem listIndent="0" listStyle="circle" listType="bulleted">4.</listItem>'
  579. );
  580. }
  581. );
  582. it(
  583. 'should copy the value for the list style attribute when indenting a single item into a nested list (changed value)',
  584. () => {
  585. setModelData( model,
  586. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  587. '<listItem listIndent="1" listStyle="disc" listType="bulleted">2.</listItem>' +
  588. '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.[]</listItem>' +
  589. '<listItem listIndent="0" listStyle="circle" listType="bulleted">4.</listItem>'
  590. );
  591. editor.execute( 'indentList' );
  592. expect( getModelData( model ) ).to.equal(
  593. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  594. '<listItem listIndent="1" listStyle="disc" listType="bulleted">2.</listItem>' +
  595. '<listItem listIndent="1" listStyle="disc" listType="bulleted">3.[]</listItem>' +
  596. '<listItem listIndent="0" listStyle="circle" listType="bulleted">4.</listItem>'
  597. );
  598. }
  599. );
  600. it( 'should copy the value for the list style attribute when indenting a single item into a nested list', () => {
  601. setModelData( model,
  602. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  603. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.[]</listItem>' +
  604. '<listItem listIndent="1" listStyle="square" listType="bulleted">3.</listItem>' +
  605. '<listItem listIndent="0" listStyle="circle" listType="bulleted">4.</listItem>'
  606. );
  607. editor.execute( 'indentList' );
  608. expect( getModelData( model ) ).to.equal(
  609. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  610. '<listItem listIndent="1" listStyle="default" listType="bulleted">2.[]</listItem>' +
  611. '<listItem listIndent="2" listStyle="square" listType="bulleted">3.</listItem>' +
  612. '<listItem listIndent="0" listStyle="circle" listType="bulleted">4.</listItem>'
  613. );
  614. } );
  615. it(
  616. 'should copy the value for the list style attribute when indenting a single item into a nested list ' +
  617. '(many nested lists check)',
  618. () => {
  619. setModelData( model,
  620. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  621. '<listItem listIndent="1" listStyle="disc" listType="bulleted">2.</listItem>' +
  622. '<listItem listIndent="2" listStyle="square" listType="bulleted">3.</listItem>' +
  623. '<listItem listIndent="0" listStyle="circle" listType="bulleted">4.[]</listItem>'
  624. );
  625. editor.execute( 'indentList' );
  626. expect( getModelData( model ) ).to.equal(
  627. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  628. '<listItem listIndent="1" listStyle="disc" listType="bulleted">2.</listItem>' +
  629. '<listItem listIndent="2" listStyle="square" listType="bulleted">3.</listItem>' +
  630. '<listItem listIndent="1" listStyle="disc" listType="bulleted">4.[]</listItem>'
  631. );
  632. }
  633. );
  634. it( 'should inherit the list style attribute from nested list if the `listType` is other than indenting element', () => {
  635. setModelData( model,
  636. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  637. '<listItem listIndent="1" listStyle="decimal" listType="numbered">2.</listItem>' +
  638. '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.[]</listItem>'
  639. );
  640. editor.execute( 'indentList' );
  641. expect( getModelData( model ) ).to.equal(
  642. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  643. '<listItem listIndent="1" listStyle="decimal" listType="numbered">2.</listItem>' +
  644. '<listItem listIndent="1" listStyle="decimal" listType="numbered">3.[]</listItem>'
  645. );
  646. } );
  647. // See: #8072.
  648. it( 'should not throw when indenting a list without any other content in the editor', () => {
  649. setModelData( model,
  650. '<listItem listIndent="0" listStyle="default" listType="bulleted">Foo</listItem>' +
  651. '<listItem listIndent="0" listStyle="default" listType="bulleted">[]</listItem>'
  652. );
  653. editor.execute( 'indentList' );
  654. expect( getModelData( model ) ).to.equal(
  655. '<listItem listIndent="0" listStyle="default" listType="bulleted">Foo</listItem>' +
  656. '<listItem listIndent="1" listStyle="default" listType="bulleted">[]</listItem>'
  657. );
  658. } );
  659. } );
  660. describe( 'outdenting lists', () => {
  661. it( 'should inherit the list style attribute from parent list (change the first nested item)', () => {
  662. setModelData( model,
  663. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  664. '<listItem listIndent="1" listStyle="default" listType="bulleted">2.[]</listItem>' +
  665. '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>'
  666. );
  667. editor.execute( 'outdentList' );
  668. expect( getModelData( model ) ).to.equal(
  669. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  670. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.[]</listItem>' +
  671. '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>'
  672. );
  673. } );
  674. it( 'should inherit the list style attribute from parent list (change the second nested item)', () => {
  675. setModelData( model,
  676. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  677. '<listItem listIndent="1" listStyle="default" listType="bulleted">2.</listItem>' +
  678. '<listItem listIndent="1" listStyle="default" listType="bulleted">3.[]</listItem>'
  679. );
  680. editor.execute( 'outdentList' );
  681. expect( getModelData( model ) ).to.equal(
  682. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  683. '<listItem listIndent="1" listStyle="default" listType="bulleted">2.</listItem>' +
  684. '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.[]</listItem>'
  685. );
  686. } );
  687. it( 'should inherit the list style attribute from parent list (modifying nested lists)', () => {
  688. setModelData( model,
  689. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  690. '<listItem listIndent="1" listStyle="default" listType="bulleted">[2.</listItem>' +
  691. '<listItem listIndent="2" listStyle="square" listType="bulleted">3.]</listItem>'
  692. );
  693. editor.execute( 'outdentList' );
  694. expect( getModelData( model ) ).to.equal(
  695. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  696. '<listItem listIndent="0" listStyle="circle" listType="bulleted">[2.</listItem>' +
  697. '<listItem listIndent="1" listStyle="square" listType="bulleted">3.]</listItem>'
  698. );
  699. } );
  700. it(
  701. 'should inherit the list style attribute from parent list (outdenting many items, including the first one in the list)',
  702. () => {
  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="2" listStyle="square" listType="bulleted">3.]</listItem>' +
  707. '<listItem listIndent="0" listStyle="circle" listType="bulleted">4.</listItem>'
  708. );
  709. editor.execute( 'outdentList' );
  710. expect( getModelData( model ) ).to.equal(
  711. '<paragraph>[1.</paragraph>' +
  712. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>' +
  713. '<listItem listIndent="1" listStyle="square" listType="bulleted">3.]</listItem>' +
  714. '<listItem listIndent="0" listStyle="circle" listType="bulleted">4.</listItem>'
  715. );
  716. }
  717. );
  718. it(
  719. 'should inherit the list style attribute from parent list (outdenting the first item that is a parent for next list)',
  720. () => {
  721. setModelData( model,
  722. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.[]</listItem>' +
  723. '<listItem listIndent="1" listStyle="default" listType="bulleted">2.</listItem>' +
  724. '<listItem listIndent="2" listStyle="square" listType="bulleted">3.</listItem>' +
  725. '<listItem listIndent="3" listStyle="disc" listType="bulleted">4.</listItem>' +
  726. '<listItem listIndent="0" listStyle="circle" listType="bulleted">5.</listItem>'
  727. );
  728. editor.execute( 'outdentList' );
  729. expect( getModelData( model ) ).to.equal(
  730. '<paragraph>1.[]</paragraph>' +
  731. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>' +
  732. '<listItem listIndent="1" listStyle="square" listType="bulleted">3.</listItem>' +
  733. '<listItem listIndent="2" listStyle="disc" listType="bulleted">4.</listItem>' +
  734. '<listItem listIndent="0" listStyle="circle" listType="bulleted">5.</listItem>'
  735. );
  736. }
  737. );
  738. it( 'should not inherit the list style if outdented the only one item in the list', () => {
  739. setModelData( model,
  740. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.[]</listItem>' +
  741. '<listItem listIndent="1" listStyle="disc" listType="bulleted">2.</listItem>' +
  742. '<listItem listIndent="2" listStyle="square" listType="bulleted">3.</listItem>'
  743. );
  744. editor.execute( 'outdentList' );
  745. expect( getModelData( model ) ).to.equal(
  746. '<paragraph>1.[]</paragraph>' +
  747. '<listItem listIndent="0" listStyle="disc" listType="bulleted">2.</listItem>' +
  748. '<listItem listIndent="1" listStyle="square" listType="bulleted">3.</listItem>'
  749. );
  750. } );
  751. it( 'should not inherit the list style if outdented the only one item in the list (a paragraph below the list)', () => {
  752. setModelData( model,
  753. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.[]</listItem>' +
  754. '<listItem listIndent="1" listStyle="disc" listType="bulleted">2.</listItem>' +
  755. '<listItem listIndent="2" listStyle="square" listType="bulleted">3.</listItem>' +
  756. '<paragraph>Foo</paragraph>'
  757. );
  758. editor.execute( 'outdentList' );
  759. expect( getModelData( model ) ).to.equal(
  760. '<paragraph>1.[]</paragraph>' +
  761. '<listItem listIndent="0" listStyle="disc" listType="bulleted">2.</listItem>' +
  762. '<listItem listIndent="1" listStyle="square" listType="bulleted">3.</listItem>' +
  763. '<paragraph>Foo</paragraph>'
  764. );
  765. } );
  766. it( 'should not inherit the list style attribute from parent list if the `listType` is other than outdenting element', () => {
  767. setModelData( model,
  768. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  769. '<listItem listIndent="1" listStyle="decimal" listType="numbered">2.[]</listItem>' +
  770. '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>'
  771. );
  772. editor.execute( 'outdentList' );
  773. expect( getModelData( model ) ).to.equal(
  774. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  775. '<listItem listIndent="0" listStyle="decimal" listType="numbered">2.[]</listItem>' +
  776. '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>'
  777. );
  778. } );
  779. it( 'should not do anything if there is no list after outdenting', () => {
  780. setModelData( model,
  781. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.[]</listItem>'
  782. );
  783. editor.execute( 'outdentList' );
  784. expect( getModelData( model ) ).to.equal(
  785. '<paragraph>1.[]</paragraph>'
  786. );
  787. } );
  788. } );
  789. describe( 'indent/outdent + undo', () => {
  790. it( 'should use the same batch for indenting a list and updating `listType` attribute', () => {
  791. setModelData( model,
  792. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  793. '<listItem listIndent="1" listStyle="default" listType="bulleted">1A.</listItem>' +
  794. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2B.</listItem>' +
  795. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.[]</listItem>' +
  796. '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>'
  797. );
  798. editor.execute( 'indentList' );
  799. editor.execute( 'undo' );
  800. expect( getModelData( model ) ).to.equal(
  801. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  802. '<listItem listIndent="1" listStyle="default" listType="bulleted">1A.</listItem>' +
  803. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2B.</listItem>' +
  804. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.[]</listItem>' +
  805. '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>'
  806. );
  807. } );
  808. it( 'should use the same batch for outdenting a list and updating `listType` attribute', () => {
  809. setModelData( model,
  810. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  811. '<listItem listIndent="1" listStyle="default" listType="bulleted">2.[]</listItem>' +
  812. '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>'
  813. );
  814. editor.execute( 'outdentList' );
  815. editor.execute( 'undo' );
  816. expect( getModelData( model ) ).to.equal(
  817. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  818. '<listItem listIndent="1" listStyle="default" listType="bulleted">2.[]</listItem>' +
  819. '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>'
  820. );
  821. } );
  822. } );
  823. describe( 'delete + undo', () => {
  824. let editor, model, view;
  825. beforeEach( () => {
  826. return VirtualTestEditor
  827. .create( {
  828. plugins: [ Paragraph, ListStyleEditing, Typing, UndoEditing ]
  829. } )
  830. .then( newEditor => {
  831. editor = newEditor;
  832. model = editor.model;
  833. view = editor.editing.view;
  834. } );
  835. } );
  836. afterEach( () => {
  837. return editor.destroy();
  838. } );
  839. // See: #7930.
  840. it( 'should restore proper list style attribute after undo merging lists', () => {
  841. // ○ 1.
  842. // ○ 2.
  843. // ○ 3.
  844. // <paragraph>
  845. // ■ 1.
  846. // ■ 2.
  847. setModelData( model,
  848. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  849. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>' +
  850. '<listItem listIndent="0" listStyle="circle" listType="bulleted">3.</listItem>' +
  851. '<paragraph>[]</paragraph>' +
  852. '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
  853. '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>'
  854. );
  855. expect( getViewData( view, { withoutSelection: true } ), 'initial data' ).to.equal(
  856. '<ul style="list-style-type:circle">' +
  857. '<li>1.</li>' +
  858. '<li>2.</li>' +
  859. '<li>3.</li>' +
  860. '</ul>' +
  861. '<p></p>' +
  862. '<ul style="list-style-type:square">' +
  863. '<li>1.</li>' +
  864. '<li>2.</li>' +
  865. '</ul>'
  866. );
  867. // After removing the paragraph.
  868. // ○ 1.
  869. // ○ 2.
  870. // ○ 3.
  871. // ○ 1.
  872. // ○ 2.
  873. editor.execute( 'delete' );
  874. expect( getViewData( view, { withoutSelection: true } ), 'executing delete' ).to.equal(
  875. '<ul style="list-style-type:circle">' +
  876. '<li>1.</li>' +
  877. '<li>2.</li>' +
  878. '<li>3.</li>' +
  879. '<li>1.</li>' +
  880. '<li>2.</li>' +
  881. '</ul>'
  882. );
  883. // After undo.
  884. // ○ 1.
  885. // ○ 2.
  886. // ○ 3.
  887. // <paragraph>
  888. // ■ 1.
  889. // ■ 2.
  890. editor.execute( 'undo' );
  891. expect( getViewData( view, { withoutSelection: true } ), 'initial data' ).to.equal(
  892. '<ul style="list-style-type:circle">' +
  893. '<li>1.</li>' +
  894. '<li>2.</li>' +
  895. '<li>3.</li>' +
  896. '</ul>' +
  897. '<p></p>' +
  898. '<ul style="list-style-type:square">' +
  899. '<li>1.</li>' +
  900. '<li>2.</li>' +
  901. '</ul>'
  902. );
  903. } );
  904. } );
  905. describe( 'todo list', () => {
  906. let editor, model;
  907. beforeEach( () => {
  908. return VirtualTestEditor
  909. .create( {
  910. // TodoListEditing is at the end by design. Check `ListStyleEditing.afterInit()` call.
  911. plugins: [ Paragraph, ListStyleEditing, TodoListEditing ]
  912. } )
  913. .then( newEditor => {
  914. editor = newEditor;
  915. model = editor.model;
  916. } );
  917. } );
  918. afterEach( () => {
  919. return editor.destroy();
  920. } );
  921. it( 'should not add the `listStyle` attribute while creating a todo list', () => {
  922. setModelData( model, '<paragraph>Foo[]</paragraph>' );
  923. editor.execute( 'todoList' );
  924. expect( getModelData( model ), '<listItem listIndent="0" listType="todo">Foo[]</listItem>' );
  925. } );
  926. it( 'should not add the `listStyle` attribute while switching the list type', () => {
  927. setModelData( model, '<listItem listIndent="0" listType="bulleted">Foo[]</listItem>' );
  928. editor.execute( 'todoList' );
  929. expect( getModelData( model ), '<listItem listIndent="0" listType="todo">Foo[]</listItem>' );
  930. } );
  931. it( 'should remove the `listStyle` attribute while switching the list type that uses the list style feature', () => {
  932. setModelData( model, '<listItem listIndent="0" listType="bulleted" listStyle="circle">Foo[]</listItem>' );
  933. editor.execute( 'todoList' );
  934. expect( getModelData( model ), '<listItem listIndent="0" listType="todo">Foo[]</listItem>' );
  935. } );
  936. it( 'should not inherit the list style attribute when inserting a todo list item', () => {
  937. setModelData( model,
  938. '<paragraph>Foo Bar.[]</paragraph>' +
  939. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
  940. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
  941. );
  942. editor.execute( 'todoList' );
  943. expect( getModelData( model ) ).to.equal(
  944. '<listItem listIndent="0" listType="todo">Foo Bar.[]</listItem>' +
  945. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
  946. '<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
  947. );
  948. } );
  949. } );
  950. describe( 'removing content between two lists', () => {
  951. let editor, model;
  952. beforeEach( () => {
  953. return VirtualTestEditor
  954. .create( {
  955. plugins: [ Paragraph, ListStyleEditing, Typing ]
  956. } )
  957. .then( newEditor => {
  958. editor = newEditor;
  959. model = editor.model;
  960. } );
  961. } );
  962. afterEach( () => {
  963. return editor.destroy();
  964. } );
  965. it( 'should not do anything while removing a letter inside a listItem', () => {
  966. setModelData( model,
  967. '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
  968. '<listItem listIndent="0" listStyle="square" listType="bulleted">2.[]</listItem>' +
  969. '<paragraph></paragraph>' +
  970. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  971. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'
  972. );
  973. editor.execute( 'delete' );
  974. expect( getModelData( model ) ).to.equal(
  975. '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
  976. '<listItem listIndent="0" listStyle="square" listType="bulleted">2[]</listItem>' +
  977. '<paragraph></paragraph>' +
  978. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  979. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'
  980. );
  981. } );
  982. it( 'should not do anything if there is a non-listItem before the removed content', () => {
  983. setModelData( model,
  984. '<paragraph>Foo</paragraph>' +
  985. '<paragraph>[]</paragraph>' +
  986. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  987. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'
  988. );
  989. editor.execute( 'delete' );
  990. expect( getModelData( model ) ).to.equal(
  991. '<paragraph>Foo[]</paragraph>' +
  992. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  993. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'
  994. );
  995. } );
  996. it( 'should not do anything if there is a non-listItem after the removed content', () => {
  997. setModelData( model,
  998. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  999. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>' +
  1000. '<paragraph>[]</paragraph>' +
  1001. '<paragraph>Foo</paragraph>'
  1002. );
  1003. editor.execute( 'delete' );
  1004. expect( getModelData( model ) ).to.equal(
  1005. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  1006. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.[]</listItem>' +
  1007. '<paragraph>Foo</paragraph>'
  1008. );
  1009. } );
  1010. it( 'should not do anything if there is no element after the removed content', () => {
  1011. setModelData( model,
  1012. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  1013. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>' +
  1014. '<paragraph>[]</paragraph>'
  1015. );
  1016. editor.execute( 'delete' );
  1017. expect( getModelData( model ) ).to.equal(
  1018. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  1019. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.[]</listItem>'
  1020. );
  1021. } );
  1022. it(
  1023. 'should modify the the `listStyle` attribute for the merged (second) list when removing content between those lists',
  1024. () => {
  1025. setModelData( model,
  1026. '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
  1027. '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +
  1028. '<paragraph>[]</paragraph>' +
  1029. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  1030. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'
  1031. );
  1032. editor.execute( 'delete' );
  1033. expect( getModelData( model ) ).to.equal(
  1034. '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
  1035. '<listItem listIndent="0" listStyle="square" listType="bulleted">2.[]</listItem>' +
  1036. '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
  1037. '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>'
  1038. );
  1039. }
  1040. );
  1041. it( 'should read the `listStyle` attribute from the most outer list', () => {
  1042. setModelData( model,
  1043. '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
  1044. '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +
  1045. '<listItem listIndent="1" listStyle="decimal" listType="numbered">2.1.</listItem>' +
  1046. '<listItem listIndent="2" listStyle="default" listType="numbered">2.1.1</listItem>' +
  1047. '<paragraph>[]</paragraph>' +
  1048. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  1049. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'
  1050. );
  1051. editor.execute( 'delete' );
  1052. expect( getModelData( model ) ).to.equal(
  1053. '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
  1054. '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +
  1055. '<listItem listIndent="1" listStyle="decimal" listType="numbered">2.1.</listItem>' +
  1056. '<listItem listIndent="2" listStyle="default" listType="numbered">2.1.1[]</listItem>' +
  1057. '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
  1058. '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>'
  1059. );
  1060. } );
  1061. it(
  1062. 'should not modify the the `listStyle` attribute for the merged (second) list if merging different `listType` attribute',
  1063. () => {
  1064. setModelData( model,
  1065. '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
  1066. '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +
  1067. '<paragraph>[]</paragraph>' +
  1068. '<listItem listIndent="0" listStyle="decimal" listType="numbered">1.</listItem>' +
  1069. '<listItem listIndent="0" listStyle="decimal" listType="numbered">2.</listItem>'
  1070. );
  1071. editor.execute( 'delete' );
  1072. expect( getModelData( model ) ).to.equal(
  1073. '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
  1074. '<listItem listIndent="0" listStyle="square" listType="bulleted">2.[]</listItem>' +
  1075. '<listItem listIndent="0" listStyle="decimal" listType="numbered">1.</listItem>' +
  1076. '<listItem listIndent="0" listStyle="decimal" listType="numbered">2.</listItem>'
  1077. );
  1078. }
  1079. );
  1080. it(
  1081. 'should modify the the `listStyle` attribute for the merged (second) list when removing content from both lists',
  1082. () => {
  1083. setModelData( model,
  1084. '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
  1085. '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +
  1086. '<listItem listIndent="0" listStyle="square" listType="bulleted">[3.</listItem>' +
  1087. '<paragraph>Foo</paragraph>' +
  1088. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.]</listItem>' +
  1089. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'
  1090. );
  1091. editor.execute( 'delete' );
  1092. expect( getModelData( model ) ).to.equal(
  1093. '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
  1094. '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +
  1095. '<listItem listIndent="0" listStyle="square" listType="bulleted">[]</listItem>' +
  1096. '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>'
  1097. );
  1098. }
  1099. );
  1100. it(
  1101. 'should modify the the `listStyle` attribute for the merged (second) list when typing over content from both lists',
  1102. () => {
  1103. setModelData( model,
  1104. '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
  1105. '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +
  1106. '<listItem listIndent="0" listStyle="square" listType="bulleted">[3.</listItem>' +
  1107. '<paragraph>Foo</paragraph>' +
  1108. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.]</listItem>' +
  1109. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'
  1110. );
  1111. editor.execute( 'input', { text: 'Foo' } );
  1112. expect( getModelData( model ) ).to.equal(
  1113. '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
  1114. '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +
  1115. '<listItem listIndent="0" listStyle="square" listType="bulleted">Foo[]</listItem>' +
  1116. '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>'
  1117. );
  1118. }
  1119. );
  1120. it(
  1121. 'should not modify the the `listStyle` if lists were not merged but the content was partially removed',
  1122. () => {
  1123. setModelData( model,
  1124. '<listItem listIndent="0" listStyle="square" listType="bulleted">111.</listItem>' +
  1125. '<listItem listIndent="0" listStyle="square" listType="bulleted">222.</listItem>' +
  1126. '<listItem listIndent="0" listStyle="square" listType="bulleted">[333.</listItem>' +
  1127. '<paragraph>Foo</paragraph>' +
  1128. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1]11.</listItem>' +
  1129. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'
  1130. );
  1131. editor.execute( 'delete' );
  1132. expect( getModelData( model ) ).to.equal(
  1133. '<listItem listIndent="0" listStyle="square" listType="bulleted">111.</listItem>' +
  1134. '<listItem listIndent="0" listStyle="square" listType="bulleted">222.</listItem>' +
  1135. '<listItem listIndent="0" listStyle="circle" listType="bulleted">[]11.</listItem>' +
  1136. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'
  1137. );
  1138. }
  1139. );
  1140. it( 'should not do anything while typing in a list item', () => {
  1141. setModelData( model,
  1142. '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
  1143. '<listItem listIndent="0" listStyle="square" listType="bulleted">2.[]</listItem>' +
  1144. '<listItem listIndent="0" listStyle="square" listType="bulleted">3.</listItem>' +
  1145. '<paragraph></paragraph>' +
  1146. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  1147. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'
  1148. );
  1149. const modelChangeStub = sinon.stub( model, 'change' ).callThrough();
  1150. simulateTyping( ' Foo' );
  1151. // Each character calls `editor.model.change()`.
  1152. expect( modelChangeStub.callCount ).to.equal( 4 );
  1153. expect( getModelData( model ) ).to.equal(
  1154. '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
  1155. '<listItem listIndent="0" listStyle="square" listType="bulleted">2. Foo[]</listItem>' +
  1156. '<listItem listIndent="0" listStyle="square" listType="bulleted">3.</listItem>' +
  1157. '<paragraph></paragraph>' +
  1158. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  1159. '<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'
  1160. );
  1161. } );
  1162. // See: #8073.
  1163. it( 'should not crash when removing a content between intended lists', () => {
  1164. setModelData( model,
  1165. '<listItem listIndent="0" listStyle="default" listType="bulleted">aaaa</listItem>' +
  1166. '<listItem listIndent="1" listStyle="default" listType="bulleted">bb[bb</listItem>' +
  1167. '<listItem listIndent="2" listStyle="default" listType="bulleted">cc]cc</listItem>' +
  1168. '<listItem listIndent="3" listStyle="default" listType="bulleted">dddd</listItem>'
  1169. );
  1170. editor.execute( 'delete' );
  1171. expect( getModelData( model ) ).to.equal(
  1172. '<listItem listIndent="0" listStyle="default" listType="bulleted">aaaa</listItem>' +
  1173. '<listItem listIndent="1" listStyle="default" listType="bulleted">bb[]cc</listItem>' +
  1174. '<listItem listIndent="2" listStyle="default" listType="bulleted">dddd</listItem>'
  1175. );
  1176. } );
  1177. it( 'should read the `listStyle` attribute from the most outer selected list while removing content between lists', () => {
  1178. setModelData( model,
  1179. '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
  1180. '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +
  1181. '<listItem listIndent="1" listStyle="decimal" listType="numbered">2.1.</listItem>' +
  1182. '<listItem listIndent="2" listStyle="lower-latin" listType="numbered">2.1.1[foo</listItem>' +
  1183. '<paragraph>Foo</paragraph>' +
  1184. '<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
  1185. '<listItem listIndent="1" listStyle="circle" listType="bulleted">bar]2.</listItem>'
  1186. );
  1187. editor.execute( 'delete' );
  1188. expect( getModelData( model ) ).to.equal(
  1189. '<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
  1190. '<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +
  1191. '<listItem listIndent="1" listStyle="decimal" listType="numbered">2.1.</listItem>' +
  1192. '<listItem listIndent="2" listStyle="lower-latin" listType="numbered">2.1.1[]2.</listItem>'
  1193. );
  1194. } );
  1195. function simulateTyping( text ) {
  1196. // While typing, every character is an atomic change.
  1197. text.split( '' ).forEach( character => {
  1198. editor.execute( 'input', {
  1199. text: character
  1200. } );
  1201. } );
  1202. }
  1203. } );
  1204. } );
  1205. } );