tableclipboard-paste.js 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218
  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. /* globals document console */
  6. import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
  7. import BlockQuoteEditing from '@ckeditor/ckeditor5-block-quote/src/blockquoteediting';
  8. import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
  9. import HorizontalLineEditing from '@ckeditor/ckeditor5-horizontal-line/src/horizontallineediting';
  10. import ImageCaptionEditing from '@ckeditor/ckeditor5-image/src/imagecaption/imagecaptionediting';
  11. import ImageEditing from '@ckeditor/ckeditor5-image/src/image/imageediting';
  12. import ListEditing from '@ckeditor/ckeditor5-list/src/listediting';
  13. import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
  14. import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
  15. import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
  16. import { assertEqualMarkup } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
  17. import { assertSelectedCells, modelTable, viewTable } from './_utils/utils';
  18. import TableEditing from '../src/tableediting';
  19. import TableCellPropertiesEditing from '../src/tablecellproperties/tablecellpropertiesediting';
  20. import TableWalker from '../src/tablewalker';
  21. import TableClipboard from '../src/tableclipboard';
  22. describe( 'table clipboard', () => {
  23. let editor, model, modelRoot, tableSelection, viewDocument, element;
  24. testUtils.createSinonSandbox();
  25. beforeEach( () => {
  26. element = document.createElement( 'div' );
  27. document.body.appendChild( element );
  28. } );
  29. afterEach( async () => {
  30. await editor.destroy();
  31. element.remove();
  32. } );
  33. describe( 'Clipboard integration - paste (selection scenarios)', () => {
  34. beforeEach( async () => {
  35. await createEditor();
  36. setModelData( model, modelTable( [
  37. [ '00[]', '01', '02', '03' ],
  38. [ '10', '11', '12', '13' ],
  39. [ '20', '21', '22', '23' ],
  40. [ '30', '31', '32', '33' ]
  41. ] ) );
  42. } );
  43. it( 'should be disabled in a readonly mode', () => {
  44. editor.isReadOnly = true;
  45. tableSelection.setCellSelection(
  46. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  47. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  48. );
  49. const data = pasteTable( [
  50. [ 'aa', 'ab' ],
  51. [ 'ba', 'bb' ]
  52. ] );
  53. editor.isReadOnly = false;
  54. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  55. [ '00', '01', '02', '03' ],
  56. [ '10', '11', '12', '13' ],
  57. [ '20', '21', '22', '23' ],
  58. [ '30', '31', '32', '33' ]
  59. ] ) );
  60. sinon.assert.calledOnce( data.preventDefault );
  61. } );
  62. it( 'should allow normal paste if no table cells are selected', () => {
  63. const data = {
  64. dataTransfer: createDataTransfer(),
  65. preventDefault: sinon.spy(),
  66. stopPropagation: sinon.spy()
  67. };
  68. data.dataTransfer.setData( 'text/html', '<p>foo</p>' );
  69. viewDocument.fire( 'paste', data );
  70. editor.isReadOnly = false;
  71. assertEqualMarkup( getModelData( model ), modelTable( [
  72. [ '00foo[]', '01', '02', '03' ],
  73. [ '10', '11', '12', '13' ],
  74. [ '20', '21', '22', '23' ],
  75. [ '30', '31', '32', '33' ]
  76. ] ) );
  77. } );
  78. it( 'should not alter model.insertContent if selectable is different from document selection', () => {
  79. model.change( writer => {
  80. writer.setSelection( modelRoot.getNodeByPath( [ 0, 0, 0 ] ), 0 );
  81. const selectedTableCells = model.createSelection( [
  82. model.createRangeOn( modelRoot.getNodeByPath( [ 0, 0, 0 ] ) ),
  83. model.createRangeOn( modelRoot.getNodeByPath( [ 0, 0, 1 ] ) ),
  84. model.createRangeOn( modelRoot.getNodeByPath( [ 0, 1, 0 ] ) ),
  85. model.createRangeOn( modelRoot.getNodeByPath( [ 0, 1, 1 ] ) )
  86. ] );
  87. const tableToInsert = editor.plugins.get( 'TableUtils' ).createTable( writer, 2, 2 );
  88. for ( const { cell } of new TableWalker( tableToInsert ) ) {
  89. writer.insertText( 'foo', cell.getChild( 0 ), 0 );
  90. }
  91. model.insertContent( tableToInsert, selectedTableCells );
  92. } );
  93. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  94. [ '', '', '02', '03' ],
  95. [ '', '', '12', '13' ],
  96. [ '20', '21', '22', '23' ],
  97. [ '30', '31', '32', '33' ]
  98. ] ) );
  99. } );
  100. it( 'should not alter model.insertContent if selection is outside table', () => {
  101. model.change( writer => {
  102. writer.insertElement( 'paragraph', modelRoot.getChild( 0 ), 'before' );
  103. writer.setSelection( modelRoot.getChild( 0 ), 'before' );
  104. } );
  105. const data = {
  106. dataTransfer: createDataTransfer(),
  107. preventDefault: sinon.spy(),
  108. stopPropagation: sinon.spy()
  109. };
  110. data.dataTransfer.setData( 'text/html', '<p>foo</p>' );
  111. viewDocument.fire( 'paste', data );
  112. editor.isReadOnly = false;
  113. assertEqualMarkup( getModelData( model ), '<paragraph>foo[]</paragraph>' + modelTable( [
  114. [ '00', '01', '02', '03' ],
  115. [ '10', '11', '12', '13' ],
  116. [ '20', '21', '22', '23' ],
  117. [ '30', '31', '32', '33' ]
  118. ] ) );
  119. } );
  120. it( 'should not alter model.insertContent if no table pasted', () => {
  121. tableSelection.setCellSelection(
  122. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  123. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  124. );
  125. const data = {
  126. dataTransfer: createDataTransfer(),
  127. preventDefault: sinon.spy(),
  128. stopPropagation: sinon.spy()
  129. };
  130. data.dataTransfer.setData( 'text/html', '<p>foo</p>' );
  131. viewDocument.fire( 'paste', data );
  132. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  133. [ 'foo', '', '02', '03' ],
  134. [ '', '', '12', '13' ],
  135. [ '20', '21', '22', '23' ],
  136. [ '30', '31', '32', '33' ]
  137. ] ) );
  138. } );
  139. it( 'should not alter model.insertContent if mixed content is pasted (table + paragraph)', () => {
  140. tableSelection.setCellSelection(
  141. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  142. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  143. );
  144. const table = viewTable( [
  145. [ 'aa', 'ab' ],
  146. [ 'ba', 'bb' ] ] );
  147. const data = {
  148. dataTransfer: createDataTransfer(),
  149. preventDefault: sinon.spy(),
  150. stopPropagation: sinon.spy()
  151. };
  152. data.dataTransfer.setData( 'text/html', `${ table }<p>foo</p>` );
  153. viewDocument.fire( 'paste', data );
  154. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  155. [ 'foo', '', '02', '03' ],
  156. [ '', '', '12', '13' ],
  157. [ '20', '21', '22', '23' ],
  158. [ '30', '31', '32', '33' ]
  159. ] ) );
  160. } );
  161. it( 'should not alter model.insertContent if mixed content is pasted (paragraph + table)', () => {
  162. tableSelection.setCellSelection(
  163. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  164. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  165. );
  166. const table = viewTable( [
  167. [ 'aa', 'ab' ],
  168. [ 'ba', 'bb' ] ] );
  169. const data = {
  170. dataTransfer: createDataTransfer(),
  171. preventDefault: sinon.spy(),
  172. stopPropagation: sinon.spy()
  173. };
  174. data.dataTransfer.setData( 'text/html', `<p>foo</p>${ table }` );
  175. viewDocument.fire( 'paste', data );
  176. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  177. [ 'foo', '', '02', '03' ],
  178. [ '', '', '12', '13' ],
  179. [ '20', '21', '22', '23' ],
  180. [ '30', '31', '32', '33' ]
  181. ] ) );
  182. } );
  183. it( 'should not alter model.insertContent if mixed content is pasted (table + table)', () => {
  184. tableSelection.setCellSelection(
  185. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  186. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  187. );
  188. const table = viewTable( [
  189. [ 'aa', 'ab' ],
  190. [ 'ba', 'bb' ] ] );
  191. const data = {
  192. dataTransfer: createDataTransfer(),
  193. preventDefault: sinon.spy(),
  194. stopPropagation: sinon.spy()
  195. };
  196. data.dataTransfer.setData( 'text/html', `${ table }${ table }` );
  197. viewDocument.fire( 'paste', data );
  198. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  199. [ '', '', '02', '03' ],
  200. [ '', '', '12', '13' ],
  201. [ '20', '21', '22', '23' ],
  202. [ '30', '31', '32', '33' ]
  203. ] ) );
  204. } );
  205. it( 'should alter model.insertContent if selectable is a document selection', () => {
  206. tableSelection.setCellSelection(
  207. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  208. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  209. );
  210. model.change( writer => {
  211. const tableToInsert = editor.plugins.get( 'TableUtils' ).createTable( writer, 2, 2 );
  212. for ( const { cell } of new TableWalker( tableToInsert ) ) {
  213. writer.insertText( 'foo', cell.getChild( 0 ), 0 );
  214. }
  215. model.insertContent( tableToInsert, editor.model.document.selection );
  216. } );
  217. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  218. [ 'foo', 'foo', '02', '03' ],
  219. [ 'foo', 'foo', '12', '13' ],
  220. [ '20', '21', '22', '23' ],
  221. [ '30', '31', '32', '33' ]
  222. ] ) );
  223. } );
  224. it( 'should block non-rectangular selection', () => {
  225. setModelData( model, modelTable( [
  226. [ { contents: '00', colspan: 3 } ],
  227. [ '10', '11', '12' ],
  228. [ '20', '21', '22' ]
  229. ] ) );
  230. tableSelection.setCellSelection(
  231. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  232. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  233. );
  234. // Catches the temporary console log in the CK_DEBUG mode.
  235. sinon.stub( console, 'log' );
  236. pasteTable( [
  237. [ 'aa', 'ab' ],
  238. [ 'ba', 'bb' ]
  239. ] );
  240. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  241. [ { contents: '00', colspan: 3 } ],
  242. [ '10', '11', '12' ],
  243. [ '20', '21', '22' ]
  244. ] ) );
  245. } );
  246. describe( 'single cell selected', () => {
  247. beforeEach( () => {
  248. setModelData( model, modelTable( [
  249. [ '00[]', '01', '02' ],
  250. [ '10', '11', '12' ],
  251. [ '20', '21', '22' ]
  252. ] ) );
  253. } );
  254. describe( 'with the selection on the middle cell', () => {
  255. beforeEach( () => {
  256. tableSelection.setCellSelection(
  257. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  258. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  259. );
  260. } );
  261. it( 'should replace the table cells (with single undo step)', () => {
  262. const batches = setupBatchWatch();
  263. pasteTable( [
  264. [ 'aa', 'ab' ],
  265. [ 'ba', 'bb' ]
  266. ] );
  267. expect( batches.size ).to.equal( 1 );
  268. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  269. [ '00', '01', '02' ],
  270. [ '10', 'aa', 'ab' ],
  271. [ '20', 'ba', 'bb' ]
  272. ] ) );
  273. } );
  274. it( 'should expand the table width and replace the table cells (with single undo step)', () => {
  275. const batches = setupBatchWatch();
  276. pasteTable( [
  277. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  278. [ 'ba', 'bb', 'bc', 'bd', 'be' ]
  279. ] );
  280. expect( batches.size ).to.equal( 1 );
  281. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  282. [ '00', '01', '02', '', '', '' ],
  283. [ '10', 'aa', 'ab', 'ac', 'ad', 'ae' ],
  284. [ '20', 'ba', 'bb', 'bc', 'bd', 'be' ]
  285. ] ) );
  286. } );
  287. it( 'should expand the table height and replace the table cells (with single undo step)', () => {
  288. const batches = setupBatchWatch();
  289. pasteTable( [
  290. [ 'aa', 'ab' ],
  291. [ 'ba', 'bb' ],
  292. [ 'ca', 'cb' ],
  293. [ 'da', 'db' ],
  294. [ 'ea', 'eb' ]
  295. ] );
  296. expect( batches.size ).to.equal( 1 );
  297. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  298. [ '00', '01', '02' ],
  299. [ '10', 'aa', 'ab' ],
  300. [ '20', 'ba', 'bb' ],
  301. [ '', 'ca', 'cb' ],
  302. [ '', 'da', 'db' ],
  303. [ '', 'ea', 'eb' ]
  304. ] ) );
  305. } );
  306. it( 'should expand the table width, height and replace the table cells (with single undo step)', () => {
  307. const batches = setupBatchWatch();
  308. pasteTable( [
  309. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  310. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  311. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  312. [ 'da', 'db', 'dc', 'dd', 'de' ],
  313. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  314. ] );
  315. expect( batches.size ).to.equal( 1 );
  316. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  317. [ '00', '01', '02', '', '', '' ],
  318. [ '10', 'aa', 'ab', 'ac', 'ad', 'ae' ],
  319. [ '20', 'ba', 'bb', 'bc', 'bd', 'be' ],
  320. [ '', 'ca', 'cb', 'cc', 'cd', 'ce' ],
  321. [ '', 'da', 'db', 'dc', 'dd', 'de' ],
  322. [ '', 'ea', 'eb', 'ec', 'ed', 'ee' ]
  323. ] ) );
  324. } );
  325. } );
  326. describe( 'with the selection on the first cell', () => {
  327. beforeEach( () => {
  328. tableSelection.setCellSelection(
  329. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  330. modelRoot.getNodeByPath( [ 0, 0, 0 ] )
  331. );
  332. } );
  333. it( 'should replace the table cells', () => {
  334. pasteTable( [
  335. [ 'aa', 'ab' ],
  336. [ 'ba', 'bb' ]
  337. ] );
  338. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  339. [ 'aa', 'ab', '02' ],
  340. [ 'ba', 'bb', '12' ],
  341. [ '20', '21', '22' ]
  342. ] ) );
  343. } );
  344. it( 'should expand the table and replace the table cells', () => {
  345. pasteTable( [
  346. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  347. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  348. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  349. [ 'da', 'db', 'dc', 'dd', 'de' ],
  350. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  351. ] );
  352. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  353. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  354. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  355. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  356. [ 'da', 'db', 'dc', 'dd', 'de' ],
  357. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  358. ] ) );
  359. } );
  360. } );
  361. describe( 'with the selection on the middle cell of the first row', () => {
  362. beforeEach( () => {
  363. tableSelection.setCellSelection(
  364. modelRoot.getNodeByPath( [ 0, 0, 1 ] ),
  365. modelRoot.getNodeByPath( [ 0, 0, 1 ] )
  366. );
  367. } );
  368. it( 'should replace the table cells', () => {
  369. pasteTable( [
  370. [ 'aa', 'ab' ],
  371. [ 'ba', 'bb' ]
  372. ] );
  373. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  374. [ '00', 'aa', 'ab' ],
  375. [ '10', 'ba', 'bb' ],
  376. [ '20', '21', '22' ]
  377. ] ) );
  378. } );
  379. it( 'should expand the table and replace the table cells', () => {
  380. pasteTable( [
  381. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  382. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  383. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  384. [ 'da', 'db', 'dc', 'dd', 'de' ],
  385. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  386. ] );
  387. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  388. [ '00', 'aa', 'ab', 'ac', 'ad', 'ae' ],
  389. [ '10', 'ba', 'bb', 'bc', 'bd', 'be' ],
  390. [ '20', 'ca', 'cb', 'cc', 'cd', 'ce' ],
  391. [ '', 'da', 'db', 'dc', 'dd', 'de' ],
  392. [ '', 'ea', 'eb', 'ec', 'ed', 'ee' ]
  393. ] ) );
  394. } );
  395. } );
  396. describe( 'with the selection on the last cell of the first row', () => {
  397. beforeEach( () => {
  398. tableSelection.setCellSelection(
  399. modelRoot.getNodeByPath( [ 0, 0, 2 ] ),
  400. modelRoot.getNodeByPath( [ 0, 0, 2 ] )
  401. );
  402. } );
  403. it( 'should replace the table cells', () => {
  404. pasteTable( [
  405. [ 'aa', 'ab' ],
  406. [ 'ba', 'bb' ]
  407. ] );
  408. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  409. [ '00', '01', 'aa', 'ab' ],
  410. [ '10', '11', 'ba', 'bb' ],
  411. [ '20', '21', '22', '' ]
  412. ] ) );
  413. } );
  414. it( 'should expand the table and replace the table cells', () => {
  415. pasteTable( [
  416. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  417. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  418. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  419. [ 'da', 'db', 'dc', 'dd', 'de' ],
  420. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  421. ] );
  422. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  423. [ '00', '01', 'aa', 'ab', 'ac', 'ad', 'ae' ],
  424. [ '10', '11', 'ba', 'bb', 'bc', 'bd', 'be' ],
  425. [ '20', '21', 'ca', 'cb', 'cc', 'cd', 'ce' ],
  426. [ '', '', 'da', 'db', 'dc', 'dd', 'de' ],
  427. [ '', '', 'ea', 'eb', 'ec', 'ed', 'ee' ]
  428. ] ) );
  429. } );
  430. } );
  431. describe( 'with the selection is on the middle cell of the first column', () => {
  432. beforeEach( () => {
  433. tableSelection.setCellSelection(
  434. modelRoot.getNodeByPath( [ 0, 1, 0 ] ),
  435. modelRoot.getNodeByPath( [ 0, 1, 0 ] )
  436. );
  437. } );
  438. it( 'should replace the table cells', () => {
  439. pasteTable( [
  440. [ 'aa', 'ab' ],
  441. [ 'ba', 'bb' ]
  442. ] );
  443. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  444. [ '00', '01', '02' ],
  445. [ 'aa', 'ab', '12' ],
  446. [ 'ba', 'bb', '22' ]
  447. ] ) );
  448. } );
  449. it( 'should expand the table and replace the table cells', () => {
  450. pasteTable( [
  451. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  452. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  453. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  454. [ 'da', 'db', 'dc', 'dd', 'de' ],
  455. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  456. ] );
  457. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  458. [ '00', '01', '02', '', '' ],
  459. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  460. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  461. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  462. [ 'da', 'db', 'dc', 'dd', 'de' ],
  463. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  464. ] ) );
  465. } );
  466. } );
  467. describe( 'with the selection is on the last cell of the first column', () => {
  468. beforeEach( () => {
  469. tableSelection.setCellSelection(
  470. modelRoot.getNodeByPath( [ 0, 2, 0 ] ),
  471. modelRoot.getNodeByPath( [ 0, 2, 0 ] )
  472. );
  473. } );
  474. it( 'should replace the table cells', () => {
  475. pasteTable( [
  476. [ 'aa', 'ab' ],
  477. [ 'ba', 'bb' ]
  478. ] );
  479. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  480. [ '00', '01', '02' ],
  481. [ '10', '11', '12' ],
  482. [ 'aa', 'ab', '22' ],
  483. [ 'ba', 'bb', '' ]
  484. ] ) );
  485. } );
  486. it( 'should expand the table and replace the table cells', () => {
  487. pasteTable( [
  488. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  489. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  490. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  491. [ 'da', 'db', 'dc', 'dd', 'de' ],
  492. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  493. ] );
  494. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  495. [ '00', '01', '02', '', '' ],
  496. [ '10', '11', '12', '', '' ],
  497. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  498. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  499. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  500. [ 'da', 'db', 'dc', 'dd', 'de' ],
  501. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  502. ] ) );
  503. } );
  504. } );
  505. describe( 'with the selection is on the last cell of the last column', () => {
  506. beforeEach( () => {
  507. tableSelection.setCellSelection(
  508. modelRoot.getNodeByPath( [ 0, 2, 2 ] ),
  509. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  510. );
  511. } );
  512. it( 'should replace the table cells', () => {
  513. pasteTable( [
  514. [ 'aa', 'ab' ],
  515. [ 'ba', 'bb' ]
  516. ] );
  517. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  518. [ '00', '01', '02', '' ],
  519. [ '10', '11', '12', '' ],
  520. [ '20', '21', 'aa', 'ab' ],
  521. [ '', '', 'ba', 'bb' ]
  522. ] ) );
  523. } );
  524. it( 'should expand the table and replace the table cells', () => {
  525. pasteTable( [
  526. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  527. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  528. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  529. [ 'da', 'db', 'dc', 'dd', 'de' ],
  530. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  531. ] );
  532. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  533. [ '00', '01', '02', '', '', '', '' ],
  534. [ '10', '11', '12', '', '', '', '' ],
  535. [ '20', '21', 'aa', 'ab', 'ac', 'ad', 'ae' ],
  536. [ '', '', 'ba', 'bb', 'bc', 'bd', 'be' ],
  537. [ '', '', 'ca', 'cb', 'cc', 'cd', 'ce' ],
  538. [ '', '', 'da', 'db', 'dc', 'dd', 'de' ],
  539. [ '', '', 'ea', 'eb', 'ec', 'ed', 'ee' ]
  540. ] ) );
  541. } );
  542. } );
  543. describe( 'the selection inside the table cell', () => {
  544. it( 'should replace the table cells when the collapsed selection is in the first cell', () => {
  545. pasteTable( [
  546. [ 'aa', 'ab' ],
  547. [ 'ba', 'bb' ]
  548. ] );
  549. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  550. [ 'aa', 'ab', '02' ],
  551. [ 'ba', 'bb', '12' ],
  552. [ '20', '21', '22' ]
  553. ] ) );
  554. } );
  555. it( 'should replace the table cells when the expanded selection is in the first cell', () => {
  556. model.change( writer => {
  557. writer.setSelection( modelRoot.getNodeByPath( [ 0, 0, 0 ] ), 'in' );
  558. } );
  559. pasteTable( [
  560. [ 'aa', 'ab' ],
  561. [ 'ba', 'bb' ]
  562. ] );
  563. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  564. [ 'aa', 'ab', '02' ],
  565. [ 'ba', 'bb', '12' ],
  566. [ '20', '21', '22' ]
  567. ] ) );
  568. } );
  569. it( 'should replace the table cells when selection is on the image inside the table cell', async () => {
  570. await editor.destroy();
  571. await createEditor( [ ImageEditing, ImageCaptionEditing ] );
  572. setModelData( model, modelTable( [
  573. [ '00', '01', '02' ],
  574. [ '10', '11', '12' ],
  575. [ '20', '21', '[<image src="/assets/sample.jpg"><caption></caption></image>]' ]
  576. ] ) );
  577. pasteTable( [
  578. [ 'aa', 'ab' ],
  579. [ 'ba', 'bb' ]
  580. ] );
  581. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  582. [ '00', '01', '02', '' ],
  583. [ '10', '11', '12', '' ],
  584. [ '20', '21', 'aa', 'ab' ],
  585. [ '', '', 'ba', 'bb' ]
  586. ] ) );
  587. } );
  588. it( 'should replace the table cells when selection is in the image caption inside the table cell', async () => {
  589. await editor.destroy();
  590. await createEditor( [ ImageEditing, ImageCaptionEditing ] );
  591. setModelData( model, modelTable( [
  592. [ '00', '01', '02' ],
  593. [ '10', '11', '12' ],
  594. [ '20', '21', '<image src="/assets/sample.jpg"><caption>fo[]o</caption></image>' ]
  595. ] ) );
  596. pasteTable( [
  597. [ 'aa', 'ab' ],
  598. [ 'ba', 'bb' ]
  599. ] );
  600. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  601. [ '00', '01', '02', '' ],
  602. [ '10', '11', '12', '' ],
  603. [ '20', '21', 'aa', 'ab' ],
  604. [ '', '', 'ba', 'bb' ]
  605. ] ) );
  606. } );
  607. } );
  608. describe( 'with spanned cells', () => {
  609. it( 'should replace the table structure', () => {
  610. // +----+----+----+----+----+----+
  611. // | 00 | 01 | 02 | 05 |
  612. // +----+----+----+----+----+----+
  613. // | 10 | 11 | 13 | 14 | 15 |
  614. // +----+ +----+----+----+
  615. // | 20 | | 23 | 25 |
  616. // +----+ +----+----+ +
  617. // | 30 | | 33 | |
  618. // +----+----+----+----+----+----+
  619. // | 40 | 42 | 43 | 44 |
  620. // +----+----+----+----+----+----+
  621. setModelData( model, modelTable( [
  622. [ '00', '01', { contents: '02', colspan: 3 }, '05' ],
  623. [ '10', { contents: '11', colspan: 2, rowspan: 3 }, '13', '14', '15' ],
  624. [ '20', { contents: '23', colspan: 2 }, { contents: '25', rowspan: 2 } ],
  625. [ '30', { contents: '33', colspan: 2 } ],
  626. [ { contents: '40', colspan: 2 }, '42', '43', { contents: '44', colspan: 2 } ]
  627. ] ) );
  628. tableSelection.setCellSelection(
  629. modelRoot.getNodeByPath( [ 0, 2, 1 ] ), // Cell 23.
  630. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  631. );
  632. // +----+----+----+----+----+
  633. // | aa | ac | ad | ae |
  634. // + + +----+----+
  635. // | | | bd | be |
  636. // +----+----+----+----+----+
  637. // | ca | cc | cd | ce |
  638. // +----+----+----+----+----+
  639. // | da | dd | de |
  640. // +----+----+----+----+ +
  641. // | ea | eb | ec | ed | |
  642. // +----+----+----+----+----+
  643. pasteTable( [
  644. [ { contents: 'aa', colspan: 2, rowspan: 2 }, { contents: 'ac', rowspan: 2 }, 'ad', 'ae' ],
  645. [ 'bd', 'be' ],
  646. [ { contents: 'ca', colspan: 2 }, 'cc', 'cd', 'ce' ],
  647. [ { contents: 'da', colspan: 3 }, 'dd', { contents: 'de', rowspan: 2 } ],
  648. [ 'ea', 'eb', 'ec', 'ed' ]
  649. ] );
  650. // +----+----+----+----+----+----+----+----+
  651. // | 00 | 01 | 02 | 05 | | |
  652. // +----+----+----+----+----+----+----+----+
  653. // | 10 | 11 | 13 | 14 | 15 | | |
  654. // +----+ +----+----+----+----+----+
  655. // | 20 | | aa | ac | ad | ae |
  656. // +----+ + + +----+----+
  657. // | 30 | | | | bd | be |
  658. // +----+----+----+----+----+----+----+----+
  659. // | 40 | 42 | ca | cc | cd | ce |
  660. // +----+----+----+----+----+----+----+----+
  661. // | | | | da | dd | de |
  662. // +----+----+----+----+----+----+----+ +
  663. // | | | | ea | eb | ec | ed | |
  664. // +----+----+----+----+----+----+----+----+
  665. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  666. [ '00', '01', { contents: '02', colspan: 3 }, '05', '', '' ],
  667. [ '10', { contents: '11', colspan: 2, rowspan: 3 }, '13', '14', '15', '', '' ],
  668. [ '20', { contents: 'aa', colspan: 2, rowspan: 2 }, { contents: 'ac', rowspan: 2 }, 'ad', 'ae' ],
  669. [ '30', 'bd', 'be' ],
  670. [ { contents: '40', colspan: 2 }, '42', { contents: 'ca', colspan: 2 }, 'cc', 'cd', 'ce' ],
  671. [ '', '', '', { contents: 'da', colspan: 3 }, 'dd', { contents: 'de', rowspan: 2 } ],
  672. [ '', '', '', 'ea', 'eb', 'ec', 'ed' ]
  673. ] ) );
  674. } );
  675. } );
  676. } );
  677. describe( 'pasted table is equal to the selected area', () => {
  678. describe( 'no spans', () => {
  679. it( 'handles simple table paste to a simple table fragment - at the beginning of a table', () => {
  680. tableSelection.setCellSelection(
  681. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  682. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  683. );
  684. pasteTable( [
  685. [ 'aa', 'ab' ],
  686. [ 'ba', 'bb' ]
  687. ] );
  688. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  689. [ 'aa', 'ab', '02', '03' ],
  690. [ 'ba', 'bb', '12', '13' ],
  691. [ '20', '21', '22', '23' ],
  692. [ '30', '31', '32', '33' ]
  693. ] ) );
  694. assertSelectedCells( model, [
  695. [ 1, 1, 0, 0 ],
  696. [ 1, 1, 0, 0 ],
  697. [ 0, 0, 0, 0 ],
  698. [ 0, 0, 0, 0 ]
  699. ] );
  700. } );
  701. it( 'handles simple table paste to a simple table fragment - at the end of a table', () => {
  702. tableSelection.setCellSelection(
  703. modelRoot.getNodeByPath( [ 0, 2, 2 ] ),
  704. modelRoot.getNodeByPath( [ 0, 3, 3 ] )
  705. );
  706. pasteTable( [
  707. [ 'aa', 'ab' ],
  708. [ 'ba', 'bb' ]
  709. ] );
  710. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  711. [ '00', '01', '02', '03' ],
  712. [ '10', '11', '12', '13' ],
  713. [ '20', '21', 'aa', 'ab' ],
  714. [ '30', '31', 'ba', 'bb' ]
  715. ] ) );
  716. assertSelectedCells( model, [
  717. [ 0, 0, 0, 0 ],
  718. [ 0, 0, 0, 0 ],
  719. [ 0, 0, 1, 1 ],
  720. [ 0, 0, 1, 1 ]
  721. ] );
  722. } );
  723. it( 'handles simple table paste to a simple table fragment - in the middle of a table', () => {
  724. tableSelection.setCellSelection(
  725. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  726. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  727. );
  728. pasteTable( [
  729. [ 'aa', 'ab' ],
  730. [ 'ba', 'bb' ]
  731. ] );
  732. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  733. [ '00', '01', '02', '03' ],
  734. [ '10', 'aa', 'ab', '13' ],
  735. [ '20', 'ba', 'bb', '23' ],
  736. [ '30', '31', '32', '33' ]
  737. ] ) );
  738. assertSelectedCells( model, [
  739. [ 0, 0, 0, 0 ],
  740. [ 0, 1, 1, 0 ],
  741. [ 0, 1, 1, 0 ],
  742. [ 0, 0, 0, 0 ]
  743. ] );
  744. } );
  745. it( 'handles simple row paste to a simple row fragment - in the middle of a table', () => {
  746. tableSelection.setCellSelection(
  747. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  748. modelRoot.getNodeByPath( [ 0, 1, 2 ] )
  749. );
  750. pasteTable( [
  751. [ 'aa', 'ab' ]
  752. ] );
  753. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  754. [ '00', '01', '02', '03' ],
  755. [ '10', 'aa', 'ab', '13' ],
  756. [ '20', '21', '22', '23' ],
  757. [ '30', '31', '32', '33' ]
  758. ] ) );
  759. assertSelectedCells( model, [
  760. [ 0, 0, 0, 0 ],
  761. [ 0, 1, 1, 0 ],
  762. [ 0, 0, 0, 0 ],
  763. [ 0, 0, 0, 0 ]
  764. ] );
  765. } );
  766. it( 'handles simple column paste to a simple column fragment - in the middle of a table', () => {
  767. tableSelection.setCellSelection(
  768. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  769. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  770. );
  771. pasteTable( [
  772. [ 'aa' ],
  773. [ 'ba' ]
  774. ] );
  775. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  776. [ '00', '01', '02', '03' ],
  777. [ '10', 'aa', '12', '13' ],
  778. [ '20', 'ba', '22', '23' ],
  779. [ '30', '31', '32', '33' ]
  780. ] ) );
  781. assertSelectedCells( model, [
  782. [ 0, 0, 0, 0 ],
  783. [ 0, 1, 0, 0 ],
  784. [ 0, 1, 0, 0 ],
  785. [ 0, 0, 0, 0 ]
  786. ] );
  787. } );
  788. it( 'handles simple table paste to a simple table fragment - whole table selected', () => {
  789. tableSelection.setCellSelection(
  790. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  791. modelRoot.getNodeByPath( [ 0, 3, 3 ] )
  792. );
  793. pasteTable( [
  794. [ 'aa', 'ab', 'ac', 'ad' ],
  795. [ 'ba', 'bb', 'bc', 'bd' ],
  796. [ 'ca', 'cb', 'cc', 'cd' ],
  797. [ 'da', 'db', 'dc', 'dd' ]
  798. ] );
  799. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  800. [ 'aa', 'ab', 'ac', 'ad' ],
  801. [ 'ba', 'bb', 'bc', 'bd' ],
  802. [ 'ca', 'cb', 'cc', 'cd' ],
  803. [ 'da', 'db', 'dc', 'dd' ]
  804. ] ) );
  805. assertSelectedCells( model, [
  806. [ 1, 1, 1, 1 ],
  807. [ 1, 1, 1, 1 ],
  808. [ 1, 1, 1, 1 ],
  809. [ 1, 1, 1, 1 ]
  810. ] );
  811. } );
  812. } );
  813. describe( 'pasted table has spans', () => {
  814. it( 'handles pasting table that has cell with colspan', () => {
  815. tableSelection.setCellSelection(
  816. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  817. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  818. );
  819. pasteTable( [
  820. [ { colspan: 2, contents: 'aa' } ],
  821. [ 'ba', 'bb' ]
  822. ] );
  823. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  824. [ '00', '01', '02', '03' ],
  825. [ '10', { colspan: 2, contents: 'aa' }, '13' ],
  826. [ '20', 'ba', 'bb', '23' ],
  827. [ '30', '31', '32', '33' ]
  828. ] ) );
  829. /* eslint-disable no-multi-spaces */
  830. assertSelectedCells( model, [
  831. [ 0, 0, 0, 0 ],
  832. [ 0, 1, 0 ],
  833. [ 0, 1, 1, 0 ],
  834. [ 0, 0, 0, 0 ]
  835. ] );
  836. /* eslint-enable no-multi-spaces */
  837. } );
  838. it( 'handles pasting table that has many cells with various colspan', () => {
  839. tableSelection.setCellSelection(
  840. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  841. modelRoot.getNodeByPath( [ 0, 3, 2 ] )
  842. );
  843. pasteTable( [
  844. [ 'aa', { colspan: 2, contents: 'ab' } ],
  845. [ { colspan: 3, contents: 'ba' } ],
  846. [ 'ca', 'cb', 'cc' ],
  847. [ { colspan: 2, contents: 'da' }, 'dc' ]
  848. ] );
  849. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  850. [ 'aa', { colspan: 2, contents: 'ab' }, '03' ],
  851. [ { colspan: 3, contents: 'ba' }, '13' ],
  852. [ 'ca', 'cb', 'cc', '23' ],
  853. [ { colspan: 2, contents: 'da' }, 'dc', '33' ]
  854. ] ) );
  855. /* eslint-disable no-multi-spaces */
  856. assertSelectedCells( model, [
  857. [ 1, 1, 0 ],
  858. [ 1, 0 ],
  859. [ 1, 1, 1, 0 ],
  860. [ 1, 1, 0 ]
  861. ] );
  862. /* eslint-enable no-multi-spaces */
  863. } );
  864. it( 'handles pasting table that has cell with rowspan', () => {
  865. tableSelection.setCellSelection(
  866. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  867. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  868. );
  869. pasteTable( [
  870. [ { rowspan: 2, contents: 'aa' }, 'ab' ],
  871. [ 'bb' ]
  872. ] );
  873. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  874. [ '00', '01', '02', '03' ],
  875. [ '10', { rowspan: 2, contents: 'aa' }, 'ab', '13' ],
  876. [ '20', 'bb', '23' ],
  877. [ '30', '31', '32', '33' ]
  878. ] ) );
  879. /* eslint-disable no-multi-spaces */
  880. assertSelectedCells( model, [
  881. [ 0, 0, 0, 0 ],
  882. [ 0, 1, 1, 0 ],
  883. [ 0, 1, 0 ],
  884. [ 0, 0, 0, 0 ]
  885. ] );
  886. /* eslint-enable no-multi-spaces */
  887. } );
  888. it( 'handles pasting table that has many cells with various rowspan', () => {
  889. tableSelection.setCellSelection(
  890. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  891. modelRoot.getNodeByPath( [ 0, 2, 3 ] )
  892. );
  893. pasteTable( [
  894. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad' ],
  895. [ { rowspan: 2, contents: 'ba' }, 'bd' ],
  896. [ 'cc', 'cd' ]
  897. ] );
  898. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  899. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad' ],
  900. [ { rowspan: 2, contents: 'ba' }, 'bd' ],
  901. [ 'cc', 'cd' ],
  902. [ '30', '31', '32', '33' ]
  903. ] ) );
  904. /* eslint-disable no-multi-spaces */
  905. assertSelectedCells( model, [
  906. [ 1, 1, 1, 1 ],
  907. [ 1, 1 ],
  908. [ 1, 1 ],
  909. [ 0, 0, 0, 0 ]
  910. ] );
  911. /* eslint-enable no-multi-spaces */
  912. } );
  913. it( 'handles pasting multi-spanned table', () => {
  914. setModelData( model, modelTable( [
  915. [ '00', '01', '02', '03', '04', '05' ],
  916. [ '10', '11', '12', '13', '14', '15' ],
  917. [ '20', '21', '22', '23', '24', '25' ],
  918. [ '30', '31', '32', '33', '34', '35' ],
  919. [ '40', '41', '42', '43', '44', '45' ]
  920. ] ) );
  921. tableSelection.setCellSelection(
  922. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  923. modelRoot.getNodeByPath( [ 0, 3, 4 ] )
  924. );
  925. // +----+----+----+----+----+
  926. // | aa | ac | ad | ae |
  927. // +----+----+----+----+ +
  928. // | ba | bb | |
  929. // +----+ +----+
  930. // | ca | | ce |
  931. // + +----+----+----+----+
  932. // | | db | dc | dd |
  933. // +----+----+----+----+----+
  934. pasteTable( [
  935. [ { contents: 'aa', colspan: 2 }, 'ac', 'ad', { contents: 'ae', rowspan: 2 } ],
  936. [ 'ba', { contents: 'bb', colspan: 3, rowspan: 2 } ],
  937. [ { contents: 'ca', rowspan: 2 }, 'ce' ],
  938. [ 'db', 'dc', { contents: 'dd', colspan: 2 } ]
  939. ] );
  940. // +----+----+----+----+----+----+
  941. // | aa | ac | ad | ae | 05 |
  942. // +----+----+----+----+ +----+
  943. // | ba | bb | | 15 |
  944. // +----+ +----+----+
  945. // | ca | | ce | 25 |
  946. // + +----+----+----+----+----+
  947. // | | db | dc | dd | 35 |
  948. // +----+----+----+----+----+----+
  949. // | 40 | 41 | 42 | 43 | 44 | 45 |
  950. // +----+----+----+----+----+----+
  951. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  952. [ { contents: 'aa', colspan: 2 }, 'ac', 'ad', { contents: 'ae', rowspan: 2 }, '05' ],
  953. [ 'ba', { contents: 'bb', colspan: 3, rowspan: 2 }, '15' ],
  954. [ { contents: 'ca', rowspan: 2 }, 'ce', '25' ],
  955. [ 'db', 'dc', { contents: 'dd', colspan: 2 }, '35' ],
  956. [ '40', '41', '42', '43', '44', '45' ]
  957. ] ) );
  958. /* eslint-disable no-multi-spaces */
  959. assertSelectedCells( model, [
  960. [ 1, 1, 1, 1, 0 ],
  961. [ 1, 1, 0 ],
  962. [ 1, 1, 0 ],
  963. [ 1, 1, 1, 0 ],
  964. [ 0, 0, 0, 0, 0, 0 ]
  965. ] );
  966. /* eslint-enable no-multi-spaces */
  967. } );
  968. } );
  969. describe( 'content table has spans', () => {
  970. it( 'handles pasting simple table over a table with colspans (no colspan exceeds selection)', () => {
  971. setModelData( model, modelTable( [
  972. [ '00[]', '01', '02', '03' ],
  973. [ { colspan: 3, contents: '10' }, '13' ],
  974. [ { colspan: 2, contents: '20' }, '22', '23' ],
  975. [ '30', '31', { colspan: 2, contents: '31' } ]
  976. ] ) );
  977. tableSelection.setCellSelection(
  978. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  979. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  980. );
  981. pasteTable( [
  982. [ 'aa', 'ab', 'ac' ],
  983. [ 'ba', 'bb', 'bc' ],
  984. [ 'ca', 'cb', 'cc' ]
  985. ] );
  986. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  987. [ 'aa', 'ab', 'ac', '03' ],
  988. [ 'ba', 'bb', 'bc', '13' ],
  989. [ 'ca', 'cb', 'cc', '23' ],
  990. [ '30', '31', { colspan: 2, contents: '31' } ]
  991. ] ) );
  992. /* eslint-disable no-multi-spaces */
  993. assertSelectedCells( model, [
  994. [ 1, 1, 1, 0 ],
  995. [ 1, 1, 1, 0 ],
  996. [ 1, 1, 1, 0 ],
  997. [ 0, 0, 0 ]
  998. ] );
  999. /* eslint-enable no-multi-spaces */
  1000. } );
  1001. it( 'handles pasting simple table over a table with rowspans (no rowspan exceeds selection)', () => {
  1002. setModelData( model, modelTable( [
  1003. [ '00', { rowspan: 3, contents: '01' }, { rowspan: 2, contents: '02' }, '03' ],
  1004. [ { rowspan: 2, contents: '10' }, '13' ],
  1005. [ '22', '23' ],
  1006. [ '30', '31', '32', '33' ]
  1007. ] ) );
  1008. tableSelection.setCellSelection(
  1009. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1010. modelRoot.getNodeByPath( [ 0, 2, 0 ] )
  1011. );
  1012. pasteTable( [
  1013. [ 'aa', 'ab', 'ac' ],
  1014. [ 'ba', 'bb', 'bc' ],
  1015. [ 'ca', 'cb', 'cc' ]
  1016. ] );
  1017. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1018. [ 'aa', 'ab', 'ac', '03' ],
  1019. [ 'ba', 'bb', 'bc', '13' ],
  1020. [ 'ca', 'cb', 'cc', '23' ],
  1021. [ '30', '31', '32', '33' ]
  1022. ] ) );
  1023. assertSelectedCells( model, [
  1024. [ 1, 1, 1, 0 ],
  1025. [ 1, 1, 1, 0 ],
  1026. [ 1, 1, 1, 0 ],
  1027. [ 0, 0, 0, 0 ]
  1028. ] );
  1029. } );
  1030. it( 'handles pasting simple table over table with multi-spans (no span exceeds selection)', () => {
  1031. // +----+----+----+----+----+----+
  1032. // | 00 | 02 | 03 | 05 |
  1033. // + + + +----+
  1034. // | | | | 15 |
  1035. // +----+----+----+ +----+
  1036. // | 20 | 21 | | 25 |
  1037. // + +----+----+----+----+----+
  1038. // | | 31 | 32 | 34 | 35 |
  1039. // +----+----+----+----+----+----+
  1040. // | 40 | 41 | 42 | 43 | 44 | 45 |
  1041. // +----+----+----+----+----+----+
  1042. setModelData( model, modelTable( [
  1043. [
  1044. { contents: '00', colspan: 2, rowspan: 2 },
  1045. { contents: '02', rowspan: 2 },
  1046. { contents: '03', colspan: 2, rowspan: 3 },
  1047. '05'
  1048. ],
  1049. [ '15' ],
  1050. [ { contents: '20', rowspan: 2 }, { contents: '21', colspan: 2 }, '25' ],
  1051. [ '31', { contents: '32', colspan: 2 }, '34', '35' ],
  1052. [ '40', '41', '42', '43', '44', '45' ]
  1053. ] ) );
  1054. tableSelection.setCellSelection(
  1055. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1056. modelRoot.getNodeByPath( [ 0, 3, 2 ] ) // Cell 34.
  1057. );
  1058. pasteTable( [
  1059. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  1060. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  1061. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  1062. [ 'da', 'db', 'dc', 'dd', 'de' ]
  1063. ] );
  1064. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1065. [ 'aa', 'ab', 'ac', 'ad', 'ae', '05' ],
  1066. [ 'ba', 'bb', 'bc', 'bd', 'be', '15' ],
  1067. [ 'ca', 'cb', 'cc', 'cd', 'ce', '25' ],
  1068. [ 'da', 'db', 'dc', 'dd', 'de', '35' ],
  1069. [ '40', '41', '42', '43', '44', '45' ]
  1070. ] ) );
  1071. assertSelectedCells( model, [
  1072. [ 1, 1, 1, 1, 1, 0 ],
  1073. [ 1, 1, 1, 1, 1, 0 ],
  1074. [ 1, 1, 1, 1, 1, 0 ],
  1075. [ 1, 1, 1, 1, 1, 0 ],
  1076. [ 0, 0, 0, 0, 0, 0 ]
  1077. ] );
  1078. } );
  1079. it( 'handles pasting simple table over a table with rowspan (rowspan before selection)', () => {
  1080. // +----+----+----+----+----+
  1081. // | 00 | 01 | 02 | 03 | 04 |
  1082. // +----+----+----+----+----+
  1083. // | 10 | 11 | 12 | 13 | 14 |
  1084. // +----+ +----+----+----+
  1085. // | 20 | | 22 | 23 | 24 |
  1086. // +----+ +----+----+----+
  1087. // | 30 | | 32 | 33 | 34 |
  1088. // +----+----+----+----+----+
  1089. // | 40 | 41 | 42 | 43 | 44 |
  1090. // +----+----+----+----+----+
  1091. setModelData( model, modelTable( [
  1092. [ '00', '01', '02', '03', '04' ],
  1093. [ '10', { contents: '11', rowspan: 3 }, '12', '13', '14' ],
  1094. [ '20', '22', '23', '24' ],
  1095. [ '30', '32', '33', '34' ],
  1096. [ '40', '41', '42', '43', '44' ]
  1097. ] ) );
  1098. tableSelection.setCellSelection(
  1099. modelRoot.getNodeByPath( [ 0, 1, 2 ] ),
  1100. modelRoot.getNodeByPath( [ 0, 3, 2 ] ) // Cell 33.
  1101. );
  1102. pasteTable( [
  1103. [ 'aa', 'ab' ],
  1104. [ 'ba', 'bb' ],
  1105. [ 'ca', 'cb' ]
  1106. ] );
  1107. // +----+----+----+----+----+
  1108. // | 00 | 01 | 02 | 03 | 04 |
  1109. // +----+----+----+----+----+
  1110. // | 10 | 11 | aa | ab | 14 |
  1111. // +----+ +----+----+----+
  1112. // | 20 | | ba | bb | 24 |
  1113. // +----+ +----+----+----+
  1114. // | 30 | | ca | cb | 34 |
  1115. // +----+----+----+----+----+
  1116. // | 40 | 41 | 42 | 43 | 44 |
  1117. // +----+----+----+----+----+
  1118. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1119. [ '00', '01', '02', '03', '04' ],
  1120. [ '10', { contents: '11', rowspan: 3 }, 'aa', 'ab', '14' ],
  1121. [ '20', 'ba', 'bb', '24' ],
  1122. [ '30', 'ca', 'cb', '34' ],
  1123. [ '40', '41', '42', '43', '44' ]
  1124. ] ) );
  1125. /* eslint-disable no-multi-spaces */
  1126. assertSelectedCells( model, [
  1127. [ 0, 0, 0, 0, 0 ],
  1128. [ 0, 0, 1, 1, 0 ],
  1129. [ 0, 1, 1, 0 ],
  1130. [ 0, 1, 1, 0 ],
  1131. [ 0, 0, 0, 0, 0 ]
  1132. ] );
  1133. /* eslint-enable no-multi-spaces */
  1134. } );
  1135. it( 'handles pasting simple table over a table with rowspans (rowspan before selection)', () => {
  1136. // +----+----+----+----+----+----+
  1137. // | 00 | 01 | 02 | 03 | 04 | 05 |
  1138. // +----+----+----+----+----+----+
  1139. // | 10 | 12 | 13 | 14 | 15 |
  1140. // +----+----+----+----+----+----+
  1141. // | 20 | 23 | 24 | 25 |
  1142. // +----+----+----+----+----+----+
  1143. // | 30 | 31 | 32 | 33 | 34 | 35 |
  1144. // +----+----+----+----+----+----+
  1145. setModelData( model, modelTable( [
  1146. [ '00', '01', '02', '03', '04', '05' ],
  1147. [ { contents: '10', colspan: 2 }, '12', '13', '14', '15' ],
  1148. [ { contents: '20', colspan: 3 }, '23', '24', '25' ],
  1149. [ '30', '31', '32', '33', '34', '35' ]
  1150. ] ) );
  1151. tableSelection.setCellSelection(
  1152. modelRoot.getNodeByPath( [ 0, 1, 2 ] ), // Cell 13.
  1153. modelRoot.getNodeByPath( [ 0, 2, 2 ] ) // Cell 24.
  1154. );
  1155. pasteTable( [
  1156. [ 'aa', 'ab' ],
  1157. [ 'ba', 'bb' ]
  1158. ] );
  1159. // +----+----+----+----+----+----+
  1160. // | 00 | 01 | 02 | 03 | 04 | 05 |
  1161. // +----+----+----+----+----+----+
  1162. // | 10 | 12 | aa | ab | 15 |
  1163. // +----+----+----+----+----+----+
  1164. // | 20 | ba | bb | 25 |
  1165. // +----+----+----+----+----+----+
  1166. // | 30 | 31 | 32 | 33 | 34 | 35 |
  1167. // +----+----+----+----+----+----+
  1168. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1169. [ '00', '01', '02', '03', '04', '05' ],
  1170. [ { contents: '10', colspan: 2 }, '12', 'aa', 'ab', '15' ],
  1171. [ { contents: '20', colspan: 3 }, 'ba', 'bb', '25' ],
  1172. [ '30', '31', '32', '33', '34', '35' ]
  1173. ] ) );
  1174. /* eslint-disable no-multi-spaces */
  1175. assertSelectedCells( model, [
  1176. [ 0, 0, 0, 0, 0, 0 ],
  1177. [ 0, 0, 1, 1, 0 ],
  1178. [ 0, 1, 1, 0 ],
  1179. [ 0, 0, 0, 0, 0, 0 ]
  1180. ] );
  1181. /* eslint-enable no-multi-spaces */
  1182. } );
  1183. // TODO: Skipped case - should allow pasting but no tools to compare areas (like in MergeCellsCommand).
  1184. it.skip( 'handles pasting table that has cell with colspan (last row in selection is spanned)', () => {
  1185. // +----+----+----+----+
  1186. // | 00 | 01 | 02 | 03 |
  1187. // +----+----+----+----+
  1188. // | 10 | 11 | 13 |
  1189. // + + +----+
  1190. // | | | 23 |
  1191. // +----+----+----+----+
  1192. // | 30 | 31 | 32 | 33 |
  1193. // +----+----+----+----+
  1194. setModelData( model, modelTable( [
  1195. [ '00', '01', '02', '03' ],
  1196. [ { contents: '10', rowspan: 2 }, { contents: '11', colspan: 2, rowspan: 2 }, '13' ],
  1197. [ '23' ],
  1198. [ '30', '31', '32', '33' ]
  1199. ] ) );
  1200. tableSelection.setCellSelection(
  1201. modelRoot.getNodeByPath( [ 0, 0, 2 ] ),
  1202. modelRoot.getNodeByPath( [ 0, 1, 0 ] )
  1203. );
  1204. pasteTable( [
  1205. [ 'aa', 'ab', 'ac' ],
  1206. [ 'ba', 'bb', 'bc' ],
  1207. [ 'ca', 'cb', 'cc' ]
  1208. ] );
  1209. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1210. [ 'aa', 'ab', 'ac', '03' ],
  1211. [ 'ba', 'bb', 'bc', '13' ],
  1212. [ 'ca', 'cb', 'cc', '23' ],
  1213. [ '30', '31', '32', '33' ]
  1214. ] ) );
  1215. assertSelectedCells( model, [
  1216. [ 1, 1, 1, 0 ],
  1217. [ 1, 1, 1, 0 ],
  1218. [ 1, 1, 1, 0 ],
  1219. [ 0, 0, 0, 0 ]
  1220. ] );
  1221. } );
  1222. } );
  1223. describe( 'content and paste tables have spans', () => {
  1224. it( 'handles pasting colspanned table over table with colspans (no colspan exceeds selection)', () => {
  1225. setModelData( model, modelTable( [
  1226. [ '00[]', '01', '02', '03' ],
  1227. [ { colspan: 3, contents: '10' }, '13' ],
  1228. [ { colspan: 2, contents: '20' }, '22', '23' ],
  1229. [ '30', '31', { colspan: 2, contents: '31' } ]
  1230. ] ) );
  1231. tableSelection.setCellSelection(
  1232. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1233. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  1234. );
  1235. pasteTable( [
  1236. [ 'aa', { colspan: 2, contents: 'ab' } ],
  1237. [ { colspan: 3, contents: 'ba' } ],
  1238. [ 'ca', 'cb', 'cc' ]
  1239. ] );
  1240. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1241. [ 'aa', { colspan: 2, contents: 'ab' }, '03' ],
  1242. [ { colspan: 3, contents: 'ba' }, '13' ],
  1243. [ 'ca', 'cb', 'cc', '23' ],
  1244. [ '30', '31', { colspan: 2, contents: '31' } ]
  1245. ] ) );
  1246. /* eslint-disable no-multi-spaces */
  1247. assertSelectedCells( model, [
  1248. [ 1, 1, 0 ],
  1249. [ 1, 0 ],
  1250. [ 1, 1, 1, 0 ],
  1251. [ 0, 0, 0 ]
  1252. ] );
  1253. /* eslint-enable no-multi-spaces */
  1254. } );
  1255. it( 'handles pasting rowspanned table over table with rowspans (no rowspan exceeds selection)', () => {
  1256. setModelData( model, modelTable( [
  1257. [ { rowspan: 3, contents: '00' }, { rowspan: 2, contents: '01' }, '02', '03' ],
  1258. [ { rowspan: 2, contents: '12' }, '13' ],
  1259. [ '21', '23' ],
  1260. [ '30', '31', '32', '33' ]
  1261. ] ) );
  1262. tableSelection.setCellSelection(
  1263. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1264. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  1265. );
  1266. pasteTable( [
  1267. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad' ],
  1268. [ { rowspan: 2, contents: 'ba' }, 'bd' ],
  1269. [ 'cc', 'cd' ]
  1270. ] );
  1271. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1272. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad' ],
  1273. [ { rowspan: 2, contents: 'ba' }, 'bd' ],
  1274. [ 'cc', 'cd' ],
  1275. [ '30', '31', '32', '33' ]
  1276. ] ) );
  1277. /* eslint-disable no-multi-spaces */
  1278. assertSelectedCells( model, [
  1279. [ 1, 1, 1, 1 ],
  1280. [ 1, 1 ],
  1281. [ 1, 1 ],
  1282. [ 0, 0, 0, 0 ]
  1283. ] );
  1284. /* eslint-enable no-multi-spaces */
  1285. } );
  1286. it( 'handles pasting multi-spanned table over table with multi-spans (no span exceeds selection)', () => {
  1287. // +----+----+----+----+----+----+
  1288. // | 00 | 02 | 03 | 05 |
  1289. // + + + +----+
  1290. // | | | | 15 |
  1291. // +----+----+----+ +----+
  1292. // | 20 | 21 | | 25 |
  1293. // + +----+----+----+----+----+
  1294. // | | 31 | 32 | 34 | 35 |
  1295. // +----+----+----+----+----+----+
  1296. // | 40 | 41 | 42 | 43 | 44 | 45 |
  1297. // +----+----+----+----+----+----+
  1298. setModelData( model, modelTable( [
  1299. [
  1300. { contents: '00', colspan: 2, rowspan: 2 },
  1301. { contents: '02', rowspan: 2 },
  1302. { contents: '03', colspan: 2, rowspan: 3 },
  1303. '05'
  1304. ],
  1305. [ '15' ],
  1306. [ { contents: '20', rowspan: 2 }, { contents: '21', colspan: 2 }, '25' ],
  1307. [ '31', { contents: '32', colspan: 2 }, '34', '35' ],
  1308. [ '40', '41', '42', '43', '44', '45' ]
  1309. ] ) );
  1310. tableSelection.setCellSelection(
  1311. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1312. modelRoot.getNodeByPath( [ 0, 3, 2 ] )
  1313. );
  1314. // +----+----+----+----+----+
  1315. // | aa | ac | ad | ae |
  1316. // +----+----+----+----+ +
  1317. // | ba | bb | |
  1318. // +----+ +----+
  1319. // | ca | | ce |
  1320. // + +----+----+----+----+
  1321. // | | db | dc | dd |
  1322. // +----+----+----+----+----+
  1323. pasteTable( [
  1324. [ { contents: 'aa', colspan: 2 }, 'ac', 'ad', { contents: 'ae', rowspan: 2 } ],
  1325. [ 'ba', { contents: 'bb', colspan: 3, rowspan: 2 } ],
  1326. [ { contents: 'ca', rowspan: 2 }, 'ce' ],
  1327. [ 'db', 'dc', { contents: 'dd', colspan: 2 } ]
  1328. ] );
  1329. // +----+----+----+----+----+----+
  1330. // | aa | ac | ad | ae | 05 |
  1331. // +----+----+----+----+ +----+
  1332. // | ba | bb | | 15 |
  1333. // +----+ +----+----+
  1334. // | ca | | ce | 25 |
  1335. // + +----+----+----+----+----+
  1336. // | | db | dc | dd | 35 |
  1337. // +----+----+----+----+----+----+
  1338. // | 40 | 41 | 42 | 43 | 44 | 45 |
  1339. // +----+----+----+----+----+----+
  1340. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1341. [ { contents: 'aa', colspan: 2 }, 'ac', 'ad', { contents: 'ae', rowspan: 2 }, '05' ],
  1342. [ 'ba', { contents: 'bb', colspan: 3, rowspan: 2 }, '15' ],
  1343. [ { contents: 'ca', rowspan: 2 }, 'ce', '25' ],
  1344. [ 'db', 'dc', { contents: 'dd', colspan: 2 }, '35' ],
  1345. [ '40', '41', '42', '43', '44', '45' ]
  1346. ] ) );
  1347. /* eslint-disable no-multi-spaces */
  1348. assertSelectedCells( model, [
  1349. [ 1, 1, 1, 1, 0 ],
  1350. [ 1, 1, 0 ],
  1351. [ 1, 1, 0 ],
  1352. [ 1, 1, 1, 0 ],
  1353. [ 0, 0, 0, 0, 0, 0 ]
  1354. ] );
  1355. /* eslint-enable no-multi-spaces */
  1356. } );
  1357. // TODO: Skipped case - should allow pasting but no tools to compare areas (like in MergeCellsCommand).
  1358. it.skip( 'handles pasting table that has cell with colspan (last row in selection is spanned)', () => {
  1359. // +----+----+----+----+
  1360. // | 00 | 01 | 02 | 03 |
  1361. // +----+----+----+----+
  1362. // | 10 | 11 | 13 |
  1363. // + + +----+
  1364. // | | | 23 |
  1365. // +----+----+----+----+
  1366. // | 30 | 31 | 32 | 33 |
  1367. // +----+----+----+----+
  1368. setModelData( model, modelTable( [
  1369. [ '00', '01', '02', '03' ],
  1370. [ { contents: '10', rowspan: 2 }, { contents: '11', colspan: 2, rowspan: 2 }, '13' ],
  1371. [ '23' ],
  1372. [ '30', '31', '32', '33' ]
  1373. ] ) );
  1374. tableSelection.setCellSelection(
  1375. modelRoot.getNodeByPath( [ 0, 0, 2 ] ),
  1376. modelRoot.getNodeByPath( [ 0, 1, 0 ] )
  1377. );
  1378. // +----+----+----+
  1379. // | aa | ab | ac |
  1380. // +----+----+----+
  1381. // | ba | bc |
  1382. // + +----+
  1383. // | | cc |
  1384. // +----+----+----+
  1385. pasteTable( [
  1386. [ 'aa', 'ab', 'ac' ],
  1387. [ { contents: 'ba', colspan: 2, rowspan: 2 }, 'bc' ],
  1388. [ 'cc' ]
  1389. ] );
  1390. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1391. [ '00', '01', '02', '03' ],
  1392. [ '10', { colspan: 2, contents: 'aa' }, '13' ],
  1393. [ '20', 'ba', 'bb', '23' ],
  1394. [ '30', '31', '32', '33' ]
  1395. ] ) );
  1396. /* eslint-disable no-multi-spaces */
  1397. assertSelectedCells( model, [
  1398. [ 0, 0, 0, 0 ],
  1399. [ 0, 1, 0 ],
  1400. [ 0, 1, 1, 0 ],
  1401. [ 0, 0, 0, 0 ]
  1402. ] );
  1403. /* eslint-enable no-multi-spaces */
  1404. } );
  1405. } );
  1406. } );
  1407. describe( 'pasted table is bigger than the selected area', () => {
  1408. describe( 'no spans', () => {
  1409. it( 'handles simple table paste to a simple table fragment - at the beginning of a table', () => {
  1410. tableSelection.setCellSelection(
  1411. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1412. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  1413. );
  1414. pasteTable( [
  1415. [ 'aa', 'ab', 'ac' ],
  1416. [ 'ba', 'bb', 'bc' ],
  1417. [ 'ca', 'cb', 'cc' ]
  1418. ] );
  1419. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1420. [ 'aa', 'ab', '02', '03' ],
  1421. [ 'ba', 'bb', '12', '13' ],
  1422. [ '20', '21', '22', '23' ],
  1423. [ '30', '31', '32', '33' ]
  1424. ] ) );
  1425. assertSelectedCells( model, [
  1426. [ 1, 1, 0, 0 ],
  1427. [ 1, 1, 0, 0 ],
  1428. [ 0, 0, 0, 0 ],
  1429. [ 0, 0, 0, 0 ]
  1430. ] );
  1431. } );
  1432. it( 'handles simple table paste to a simple table fragment - at the end of a table', () => {
  1433. tableSelection.setCellSelection(
  1434. modelRoot.getNodeByPath( [ 0, 2, 2 ] ),
  1435. modelRoot.getNodeByPath( [ 0, 3, 3 ] )
  1436. );
  1437. pasteTable( [
  1438. [ 'aa', 'ab', 'ac' ],
  1439. [ 'ba', 'bb', 'bc' ],
  1440. [ 'ca', 'cb', 'cc' ]
  1441. ] );
  1442. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1443. [ '00', '01', '02', '03' ],
  1444. [ '10', '11', '12', '13' ],
  1445. [ '20', '21', 'aa', 'ab' ],
  1446. [ '30', '31', 'ba', 'bb' ]
  1447. ] ) );
  1448. assertSelectedCells( model, [
  1449. [ 0, 0, 0, 0 ],
  1450. [ 0, 0, 0, 0 ],
  1451. [ 0, 0, 1, 1 ],
  1452. [ 0, 0, 1, 1 ]
  1453. ] );
  1454. } );
  1455. it( 'handles simple table paste to a simple table fragment - in the middle of a table', () => {
  1456. tableSelection.setCellSelection(
  1457. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  1458. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  1459. );
  1460. pasteTable( [
  1461. [ 'aa', 'ab', 'ac' ],
  1462. [ 'ba', 'bb', 'bc' ],
  1463. [ 'ca', 'cb', 'cc' ]
  1464. ] );
  1465. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1466. [ '00', '01', '02', '03' ],
  1467. [ '10', 'aa', 'ab', '13' ],
  1468. [ '20', 'ba', 'bb', '23' ],
  1469. [ '30', '31', '32', '33' ]
  1470. ] ) );
  1471. assertSelectedCells( model, [
  1472. [ 0, 0, 0, 0 ],
  1473. [ 0, 1, 1, 0 ],
  1474. [ 0, 1, 1, 0 ],
  1475. [ 0, 0, 0, 0 ]
  1476. ] );
  1477. } );
  1478. it( 'handles paste to a simple row fragment - in the middle of a table', () => {
  1479. tableSelection.setCellSelection(
  1480. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  1481. modelRoot.getNodeByPath( [ 0, 1, 2 ] )
  1482. );
  1483. pasteTable( [
  1484. [ 'aa', 'ab', 'ac' ],
  1485. [ 'ba', 'bb', 'bc' ],
  1486. [ 'ca', 'cb', 'cc' ]
  1487. ] );
  1488. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1489. [ '00', '01', '02', '03' ],
  1490. [ '10', 'aa', 'ab', '13' ],
  1491. [ '20', '21', '22', '23' ],
  1492. [ '30', '31', '32', '33' ]
  1493. ] ) );
  1494. assertSelectedCells( model, [
  1495. [ 0, 0, 0, 0 ],
  1496. [ 0, 1, 1, 0 ],
  1497. [ 0, 0, 0, 0 ],
  1498. [ 0, 0, 0, 0 ]
  1499. ] );
  1500. } );
  1501. it( 'handles paste to a simple column fragment - in the middle of a table', () => {
  1502. tableSelection.setCellSelection(
  1503. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  1504. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  1505. );
  1506. pasteTable( [
  1507. [ 'aa', 'ab', 'ac' ],
  1508. [ 'ba', 'bb', 'bc' ],
  1509. [ 'ca', 'cb', 'cc' ]
  1510. ] );
  1511. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1512. [ '00', '01', '02', '03' ],
  1513. [ '10', 'aa', '12', '13' ],
  1514. [ '20', 'ba', '22', '23' ],
  1515. [ '30', '31', '32', '33' ]
  1516. ] ) );
  1517. assertSelectedCells( model, [
  1518. [ 0, 0, 0, 0 ],
  1519. [ 0, 1, 0, 0 ],
  1520. [ 0, 1, 0, 0 ],
  1521. [ 0, 0, 0, 0 ]
  1522. ] );
  1523. } );
  1524. it( 'handles simple table paste to a simple table fragment - whole table selected', () => {
  1525. tableSelection.setCellSelection(
  1526. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1527. modelRoot.getNodeByPath( [ 0, 3, 3 ] )
  1528. );
  1529. pasteTable( [
  1530. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  1531. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  1532. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  1533. [ 'da', 'db', 'dc', 'dd', 'de' ],
  1534. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  1535. ] );
  1536. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1537. [ 'aa', 'ab', 'ac', 'ad' ],
  1538. [ 'ba', 'bb', 'bc', 'bd' ],
  1539. [ 'ca', 'cb', 'cc', 'cd' ],
  1540. [ 'da', 'db', 'dc', 'dd' ]
  1541. ] ) );
  1542. assertSelectedCells( model, [
  1543. [ 1, 1, 1, 1 ],
  1544. [ 1, 1, 1, 1 ],
  1545. [ 1, 1, 1, 1 ],
  1546. [ 1, 1, 1, 1 ]
  1547. ] );
  1548. } );
  1549. } );
  1550. describe( 'pasted table has spans', () => {
  1551. it( 'handles pasting table that has cell with colspan', () => {
  1552. tableSelection.setCellSelection(
  1553. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  1554. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  1555. );
  1556. pasteTable( [
  1557. [ { colspan: 3, contents: 'aa' } ],
  1558. [ 'ba', 'bb', 'bc' ]
  1559. ] );
  1560. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1561. [ '00', '01', '02', '03' ],
  1562. [ '10', { colspan: 2, contents: 'aa' }, '13' ],
  1563. [ '20', 'ba', 'bb', '23' ],
  1564. [ '30', '31', '32', '33' ]
  1565. ] ) );
  1566. /* eslint-disable no-multi-spaces */
  1567. assertSelectedCells( model, [
  1568. [ 0, 0, 0, 0 ],
  1569. [ 0, 1, 0 ],
  1570. [ 0, 1, 1, 0 ],
  1571. [ 0, 0, 0, 0 ]
  1572. ] );
  1573. /* eslint-enable no-multi-spaces */
  1574. } );
  1575. it( 'handles pasting table that has many cells with various colspan', () => {
  1576. tableSelection.setCellSelection(
  1577. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1578. modelRoot.getNodeByPath( [ 0, 3, 2 ] )
  1579. );
  1580. pasteTable( [
  1581. [ 'aa', { colspan: 3, contents: 'ab' } ],
  1582. [ { colspan: 4, contents: 'ba' } ],
  1583. [ 'ca', 'cb', 'cc', 'cd' ],
  1584. [ { colspan: 2, contents: 'da' }, 'dc', 'dd' ]
  1585. ] );
  1586. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1587. [ 'aa', { colspan: 2, contents: 'ab' }, '03' ],
  1588. [ { colspan: 3, contents: 'ba' }, '13' ],
  1589. [ 'ca', 'cb', 'cc', '23' ],
  1590. [ { colspan: 2, contents: 'da' }, 'dc', '33' ]
  1591. ] ) );
  1592. /* eslint-disable no-multi-spaces */
  1593. assertSelectedCells( model, [
  1594. [ 1, 1, 0 ],
  1595. [ 1, 0 ],
  1596. [ 1, 1, 1, 0 ],
  1597. [ 1, 1, 0 ]
  1598. ] );
  1599. /* eslint-enable no-multi-spaces */
  1600. } );
  1601. it( 'handles pasting table that has cell with rowspan', () => {
  1602. tableSelection.setCellSelection(
  1603. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  1604. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  1605. );
  1606. pasteTable( [
  1607. [ { rowspan: 3, contents: 'aa' }, 'ab' ],
  1608. [ 'bb' ],
  1609. [ 'cb' ]
  1610. ] );
  1611. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1612. [ '00', '01', '02', '03' ],
  1613. [ '10', { rowspan: 2, contents: 'aa' }, 'ab', '13' ],
  1614. [ '20', 'bb', '23' ],
  1615. [ '30', '31', '32', '33' ]
  1616. ] ) );
  1617. /* eslint-disable no-multi-spaces */
  1618. assertSelectedCells( model, [
  1619. [ 0, 0, 0, 0 ],
  1620. [ 0, 1, 1, 0 ],
  1621. [ 0, 1, 0 ],
  1622. [ 0, 0, 0, 0 ]
  1623. ] );
  1624. /* eslint-enable no-multi-spaces */
  1625. } );
  1626. it( 'handles pasting table that has many cells with various rowspan', () => {
  1627. tableSelection.setCellSelection(
  1628. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1629. modelRoot.getNodeByPath( [ 0, 2, 3 ] )
  1630. );
  1631. pasteTable( [
  1632. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad', 'ae' ],
  1633. [ { rowspan: 3, contents: 'ba' }, 'bd', 'be' ],
  1634. [ 'cc', 'cd', 'ce' ],
  1635. [ 'da', 'db', 'dc', 'dd' ]
  1636. ] );
  1637. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1638. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad' ],
  1639. [ { rowspan: 2, contents: 'ba' }, 'bd' ],
  1640. [ 'cc', 'cd' ],
  1641. [ '30', '31', '32', '33' ]
  1642. ] ) );
  1643. /* eslint-disable no-multi-spaces */
  1644. assertSelectedCells( model, [
  1645. [ 1, 1, 1, 1 ],
  1646. [ 1, 1 ],
  1647. [ 1, 1 ],
  1648. [ 0, 0, 0, 0 ]
  1649. ] );
  1650. /* eslint-enable no-multi-spaces */
  1651. } );
  1652. it( 'handles pasting multi-spanned table', () => {
  1653. setModelData( model, modelTable( [
  1654. [ '00', '01', '02', '03', '04', '05' ],
  1655. [ '10', '11', '12', '13', '14', '15' ],
  1656. [ '20', '21', '22', '23', '24', '25' ],
  1657. [ '30', '31', '32', '33', '34', '35' ],
  1658. [ '40', '41', '42', '43', '44', '45' ]
  1659. ] ) );
  1660. tableSelection.setCellSelection(
  1661. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  1662. modelRoot.getNodeByPath( [ 0, 3, 3 ] )
  1663. );
  1664. // +----+----+----+----+----+
  1665. // | aa | ac | ad | ae |
  1666. // +----+----+----+----+ +
  1667. // | ba | bb | |
  1668. // +----+ +----+
  1669. // | ca | | ce |
  1670. // + +----+----+----+----+
  1671. // | | db | dc | dd |
  1672. // +----+----+----+----+----+
  1673. pasteTable( [
  1674. [ { contents: 'aa', colspan: 2 }, 'ac', 'ad', { contents: 'ae', rowspan: 2 } ],
  1675. [ 'ba', { contents: 'bb', colspan: 3, rowspan: 2 } ],
  1676. [ { contents: 'ca', rowspan: 2 }, 'ce' ],
  1677. [ 'db', 'dc', { contents: 'dd', colspan: 2 } ]
  1678. ] );
  1679. // +----+----+----+----+----+----+
  1680. // | 00 | 01 | 02 | 03 | 04 | 05 |
  1681. // +----+----+----+----+----+----+
  1682. // | 10 | aa | ac | 14 | 15 |
  1683. // +----+----+----+----+----+----+
  1684. // | 20 | ba | bb | 24 | 25 |
  1685. // +----+----+ +----+----+
  1686. // | 30 | ca | | 34 | 35 |
  1687. // +----+----+----+----+----+----+
  1688. // | 40 | 41 | 42 | 43 | 44 | 45 |
  1689. // +----+----+----+----+----+----+
  1690. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1691. [ '00', '01', '02', '03', '04', '05' ],
  1692. [ '10', { contents: 'aa', colspan: 2 }, 'ac', '14', '15' ],
  1693. [ '20', 'ba', { contents: 'bb', colspan: 2, rowspan: 2 }, '24', '25' ],
  1694. [ '30', 'ca', '34', '35' ],
  1695. [ '40', '41', '42', '43', '44', '45' ]
  1696. ] ) );
  1697. /* eslint-disable no-multi-spaces */
  1698. assertSelectedCells( model, [
  1699. [ 0, 0, 0, 0, 0, 0 ],
  1700. [ 0, 1, 1, 0, 0 ],
  1701. [ 0, 1, 1, 0, 0 ],
  1702. [ 0, 1, 0, 0 ],
  1703. [ 0, 0, 0, 0, 0, 0 ]
  1704. ] );
  1705. /* eslint-enable no-multi-spaces */
  1706. } );
  1707. } );
  1708. } );
  1709. describe( 'pasted table is smaller than the selected area', () => {
  1710. it( 'blocks this case', () => {
  1711. tableSelection.setCellSelection(
  1712. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1713. modelRoot.getNodeByPath( [ 0, 3, 3 ] )
  1714. );
  1715. // Catches the temporary console log in the CK_DEBUG mode.
  1716. sinon.stub( console, 'log' );
  1717. pasteTable( [
  1718. [ 'aa', 'ab' ],
  1719. [ 'ba', 'bb' ]
  1720. ] );
  1721. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1722. [ '00', '01', '02', '03' ],
  1723. [ '10', '11', '12', '13' ],
  1724. [ '20', '21', '22', '23' ],
  1725. [ '30', '31', '32', '33' ]
  1726. ] ) );
  1727. assertSelectedCells( model, [
  1728. [ 1, 1, 1, 1 ],
  1729. [ 1, 1, 1, 1 ],
  1730. [ 1, 1, 1, 1 ],
  1731. [ 1, 1, 1, 1 ]
  1732. ] );
  1733. } );
  1734. } );
  1735. } );
  1736. describe( 'Clipboard integration - paste (content scenarios)', () => {
  1737. it( 'handles multiple paragraphs in table cell', async () => {
  1738. await createEditor();
  1739. setModelData( model, modelTable( [
  1740. [ '00', '01', '02' ],
  1741. [ '01', '11', '12' ],
  1742. [ '02', '21', '22' ]
  1743. ] ) );
  1744. tableSelection.setCellSelection(
  1745. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1746. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  1747. );
  1748. pasteTable( [
  1749. [ '<p>a</p><p>a</p><p>a</p>', 'ab' ],
  1750. [ 'ba', 'bb' ]
  1751. ] );
  1752. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1753. [ '<paragraph>a</paragraph><paragraph>a</paragraph><paragraph>a</paragraph>', 'ab', '02' ],
  1754. [ 'ba', 'bb', '12' ],
  1755. [ '02', '21', '22' ]
  1756. ] ) );
  1757. } );
  1758. it( 'handles image in table cell', async () => {
  1759. await createEditor( [ ImageEditing, ImageCaptionEditing ] );
  1760. setModelData( model, modelTable( [
  1761. [ '00', '01', '02' ],
  1762. [ '01', '11', '12' ],
  1763. [ '02', '21', '22' ]
  1764. ] ) );
  1765. tableSelection.setCellSelection(
  1766. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1767. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  1768. );
  1769. pasteTable( [
  1770. [ '<img src="/assets/sample.jpg">', 'ab' ],
  1771. [ 'ba', 'bb' ]
  1772. ] );
  1773. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1774. [ '<image src="/assets/sample.jpg"><caption></caption></image>', 'ab', '02' ],
  1775. [ 'ba', 'bb', '12' ],
  1776. [ '02', '21', '22' ]
  1777. ] ) );
  1778. } );
  1779. it( 'handles mixed nested content in table cell', async () => {
  1780. await createEditor( [ ImageEditing, ImageCaptionEditing, BlockQuoteEditing, HorizontalLineEditing, ListEditing ] );
  1781. setModelData( model, modelTable( [
  1782. [ '00', '01', '02' ],
  1783. [ '01', '11', '12' ],
  1784. [ '02', '21', '22' ]
  1785. ] ) );
  1786. tableSelection.setCellSelection(
  1787. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1788. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  1789. );
  1790. const img = '<img src="/assets/sample.jpg">';
  1791. const list = '<ul><li>foo</li><li>bar</li></ul>';
  1792. const blockquote = `<blockquote><p>baz</p>${ list }</blockquote>`;
  1793. pasteTable( [
  1794. [ `${ img }${ list }${ blockquote }`, 'ab' ],
  1795. [ 'ba', 'bb' ]
  1796. ] );
  1797. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1798. [
  1799. '<image src="/assets/sample.jpg"><caption></caption></image>' +
  1800. '<listItem listIndent="0" listType="bulleted">foo</listItem>' +
  1801. '<listItem listIndent="0" listType="bulleted">bar</listItem>' +
  1802. '<blockQuote>' +
  1803. '<paragraph>baz</paragraph>' +
  1804. '<listItem listIndent="0" listType="bulleted">foo</listItem>' +
  1805. '<listItem listIndent="0" listType="bulleted">bar</listItem>' +
  1806. '</blockQuote>',
  1807. 'ab',
  1808. '02' ],
  1809. [ 'ba', 'bb', '12' ],
  1810. [ '02', '21', '22' ]
  1811. ] ) );
  1812. } );
  1813. it( 'handles table cell properties', async () => {
  1814. await createEditor( [ TableCellPropertiesEditing ] );
  1815. setModelData( model, modelTable( [
  1816. [ '00', '01', '02' ],
  1817. [ '01', '11', '12' ],
  1818. [ '02', '21', '22' ]
  1819. ] ) );
  1820. tableSelection.setCellSelection(
  1821. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1822. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  1823. );
  1824. pasteTable( [
  1825. [ { contents: 'aa', style: 'border:1px solid #f00;background:#ba7;width:1337px' }, 'ab' ],
  1826. [ 'ba', 'bb' ]
  1827. ] );
  1828. const tableCell = model.document.getRoot().getNodeByPath( [ 0, 0, 0 ] );
  1829. expect( tableCell.getAttribute( 'borderColor' ) ).to.deep.equal( {
  1830. top: '#f00',
  1831. right: '#f00',
  1832. bottom: '#f00',
  1833. left: '#f00'
  1834. } );
  1835. expect( tableCell.getAttribute( 'borderStyle' ) ).to.deep.equal( {
  1836. top: 'solid',
  1837. right: 'solid',
  1838. bottom: 'solid',
  1839. left: 'solid'
  1840. } );
  1841. expect( tableCell.getAttribute( 'borderWidth' ) ).to.deep.equal( {
  1842. top: '1px',
  1843. right: '1px',
  1844. bottom: '1px',
  1845. left: '1px'
  1846. } );
  1847. expect( tableCell.getAttribute( 'backgroundColor' ) ).to.equal( '#ba7' );
  1848. expect( tableCell.getAttribute( 'width' ) ).to.equal( '1337px' );
  1849. } );
  1850. it( 'discards table properties', async () => {
  1851. await createEditor( [ TableCellPropertiesEditing ] );
  1852. setModelData( model, modelTable( [
  1853. [ '00', '01', '02' ],
  1854. [ '01', '11', '12' ],
  1855. [ '02', '21', '22' ]
  1856. ] ) );
  1857. tableSelection.setCellSelection(
  1858. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1859. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  1860. );
  1861. const tableStyle = 'border:1px solid #f00;background:#ba7;width:1337px';
  1862. const pastedTable = `<table style="${ tableStyle }"><tr><td>aa</td><td>ab</td></tr><tr><td>ba</td><td>bb</td></tr></table>`;
  1863. const data = {
  1864. dataTransfer: createDataTransfer(),
  1865. preventDefault: sinon.spy(),
  1866. stopPropagation: sinon.spy()
  1867. };
  1868. data.dataTransfer.setData( 'text/html', pastedTable );
  1869. viewDocument.fire( 'paste', data );
  1870. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1871. [ 'aa', 'ab', '02' ],
  1872. [ 'ba', 'bb', '12' ],
  1873. [ '02', '21', '22' ]
  1874. ] ) );
  1875. } );
  1876. } );
  1877. async function createEditor( extraPlugins = [] ) {
  1878. editor = await ClassicTestEditor.create( element, {
  1879. plugins: [ TableEditing, TableClipboard, Paragraph, Clipboard, ...extraPlugins ]
  1880. } );
  1881. model = editor.model;
  1882. modelRoot = model.document.getRoot();
  1883. viewDocument = editor.editing.view.document;
  1884. tableSelection = editor.plugins.get( 'TableSelection' );
  1885. }
  1886. function pasteTable( tableData ) {
  1887. const data = {
  1888. dataTransfer: createDataTransfer(),
  1889. preventDefault: sinon.spy(),
  1890. stopPropagation: sinon.spy()
  1891. };
  1892. data.dataTransfer.setData( 'text/html', viewTable( tableData ) );
  1893. viewDocument.fire( 'paste', data );
  1894. return data;
  1895. }
  1896. function createDataTransfer() {
  1897. const store = new Map();
  1898. return {
  1899. setData( type, data ) {
  1900. store.set( type, data );
  1901. },
  1902. getData( type ) {
  1903. return store.get( type );
  1904. }
  1905. };
  1906. }
  1907. function setupBatchWatch() {
  1908. const createdBatches = new Set();
  1909. model.on( 'applyOperation', ( evt, [ operation ] ) => {
  1910. if ( operation.isDocumentOperation ) {
  1911. createdBatches.add( operation.batch );
  1912. }
  1913. } );
  1914. return createdBatches;
  1915. }
  1916. } );