integration.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. /**
  2. * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* global document */
  6. import BlockQuote from '../src/blockquote';
  7. import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
  8. import Image from '@ckeditor/ckeditor5-image/src/image';
  9. import ImageCaption from '@ckeditor/ckeditor5-image/src/imagecaption';
  10. import List from '@ckeditor/ckeditor5-list/src/list';
  11. import Enter from '@ckeditor/ckeditor5-enter/src/enter';
  12. import Delete from '@ckeditor/ckeditor5-typing/src/delete';
  13. import Heading from '@ckeditor/ckeditor5-heading/src/heading';
  14. import Table from '@ckeditor/ckeditor5-table/src/table';
  15. import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
  16. import {
  17. parse as parseModel,
  18. getData as getModelData,
  19. setData as setModelData
  20. } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
  21. describe( 'BlockQuote integration', () => {
  22. let editor, model, element, viewDocument;
  23. beforeEach( () => {
  24. element = document.createElement( 'div' );
  25. document.body.appendChild( element );
  26. return ClassicTestEditor
  27. .create( element, {
  28. plugins: [ BlockQuote, Paragraph, Image, ImageCaption, List, Enter, Delete, Heading, Table ]
  29. } )
  30. .then( newEditor => {
  31. editor = newEditor;
  32. model = editor.model;
  33. viewDocument = editor.editing.view.document;
  34. } );
  35. } );
  36. afterEach( () => {
  37. element.remove();
  38. return editor.destroy();
  39. } );
  40. describe( 'enter key support', () => {
  41. function fakeEventData() {
  42. return {
  43. preventDefault: sinon.spy()
  44. };
  45. }
  46. it( 'does nothing if selection is in an empty block but not in a block quote', () => {
  47. const data = fakeEventData();
  48. const execSpy = sinon.spy( editor, 'execute' );
  49. setModelData( model, '<paragraph>x</paragraph><paragraph>[]</paragraph><paragraph>x</paragraph>' );
  50. viewDocument.fire( 'enter', data );
  51. // Only enter command should be executed.
  52. expect( data.preventDefault.called ).to.be.true;
  53. expect( execSpy.calledOnce ).to.be.true;
  54. expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'enter' );
  55. } );
  56. it( 'does nothing if selection is in a non-empty block (at the end) in a block quote', () => {
  57. const data = fakeEventData();
  58. const execSpy = sinon.spy( editor, 'execute' );
  59. setModelData( model, '<blockQuote><paragraph>xx[]</paragraph></blockQuote>' );
  60. viewDocument.fire( 'enter', data );
  61. // Only enter command should be executed.
  62. expect( data.preventDefault.called ).to.be.true;
  63. expect( execSpy.calledOnce ).to.be.true;
  64. expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'enter' );
  65. } );
  66. it( 'does nothing if selection is in a non-empty block (at the beginning) in a block quote', () => {
  67. const data = fakeEventData();
  68. const execSpy = sinon.spy( editor, 'execute' );
  69. setModelData( model, '<blockQuote><paragraph>[]xx</paragraph></blockQuote>' );
  70. viewDocument.fire( 'enter', data );
  71. // Only enter command should be executed.
  72. expect( data.preventDefault.called ).to.be.true;
  73. expect( execSpy.calledOnce ).to.be.true;
  74. expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'enter' );
  75. } );
  76. it( 'does nothing if selection is not collapsed', () => {
  77. const data = fakeEventData();
  78. const execSpy = sinon.spy( editor, 'execute' );
  79. setModelData( model, '<blockQuote><paragraph>[</paragraph><paragraph>]</paragraph></blockQuote>' );
  80. viewDocument.fire( 'enter', data );
  81. // Only enter command should be executed.
  82. expect( data.preventDefault.called ).to.be.true;
  83. expect( execSpy.calledOnce ).to.be.true;
  84. expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'enter' );
  85. } );
  86. it( 'does not interfere with a similar handler in the list feature', () => {
  87. const data = fakeEventData();
  88. setModelData( model,
  89. '<paragraph>x</paragraph>' +
  90. '<blockQuote>' +
  91. '<listItem listIndent="0" listType="bulleted">a</listItem>' +
  92. '<listItem listIndent="0" listType="bulleted">[]</listItem>' +
  93. '</blockQuote>' +
  94. '<paragraph>x</paragraph>'
  95. );
  96. viewDocument.fire( 'enter', data );
  97. expect( data.preventDefault.called ).to.be.true;
  98. expect( getModelData( model ) ).to.equal(
  99. '<paragraph>x</paragraph>' +
  100. '<blockQuote>' +
  101. '<listItem listIndent="0" listType="bulleted">a</listItem>' +
  102. '<paragraph>[]</paragraph>' +
  103. '</blockQuote>' +
  104. '<paragraph>x</paragraph>'
  105. );
  106. } );
  107. it( 'escapes block quote if selection is in an empty block in an empty block quote', () => {
  108. const data = fakeEventData();
  109. const execSpy = sinon.spy( editor, 'execute' );
  110. setModelData( model, '<paragraph>x</paragraph><blockQuote><paragraph>[]</paragraph></blockQuote><paragraph>x</paragraph>' );
  111. viewDocument.fire( 'enter', data );
  112. expect( data.preventDefault.called ).to.be.true;
  113. expect( execSpy.calledOnce ).to.be.true;
  114. expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'blockQuote' );
  115. expect( getModelData( model ) ).to.equal( '<paragraph>x</paragraph><paragraph>[]</paragraph><paragraph>x</paragraph>' );
  116. } );
  117. it( 'escapes block quote if selection is in an empty block in the middle of a block quote', () => {
  118. const data = fakeEventData();
  119. const execSpy = sinon.spy( editor, 'execute' );
  120. setModelData( model,
  121. '<paragraph>x</paragraph>' +
  122. '<blockQuote><paragraph>a</paragraph><paragraph>[]</paragraph><paragraph>b</paragraph></blockQuote>' +
  123. '<paragraph>x</paragraph>'
  124. );
  125. viewDocument.fire( 'enter', data );
  126. expect( data.preventDefault.called ).to.be.true;
  127. expect( execSpy.calledOnce ).to.be.true;
  128. expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'blockQuote' );
  129. expect( getModelData( model ) ).to.equal(
  130. '<paragraph>x</paragraph>' +
  131. '<blockQuote><paragraph>a</paragraph></blockQuote>' +
  132. '<paragraph>[]</paragraph>' +
  133. '<blockQuote><paragraph>b</paragraph></blockQuote>' +
  134. '<paragraph>x</paragraph>'
  135. );
  136. } );
  137. it( 'escapes block quote if selection is in an empty block at the end of a block quote', () => {
  138. const data = fakeEventData();
  139. const execSpy = sinon.spy( editor, 'execute' );
  140. setModelData( model,
  141. '<paragraph>x</paragraph>' +
  142. '<blockQuote><paragraph>a</paragraph><paragraph>[]</paragraph></blockQuote>' +
  143. '<paragraph>x</paragraph>'
  144. );
  145. viewDocument.fire( 'enter', data );
  146. expect( data.preventDefault.called ).to.be.true;
  147. expect( execSpy.calledOnce ).to.be.true;
  148. expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'blockQuote' );
  149. expect( getModelData( model ) ).to.equal(
  150. '<paragraph>x</paragraph>' +
  151. '<blockQuote><paragraph>a</paragraph></blockQuote>' +
  152. '<paragraph>[]</paragraph>' +
  153. '<paragraph>x</paragraph>'
  154. );
  155. } );
  156. it( 'scrolls the view document to the selection after the command is executed', () => {
  157. const data = fakeEventData();
  158. const execSpy = sinon.spy( editor, 'execute' );
  159. const scrollSpy = sinon.stub( editor.editing.view, 'scrollToTheSelection' );
  160. setModelData( model,
  161. '<paragraph>x</paragraph>' +
  162. '<blockQuote><paragraph>a</paragraph><paragraph>[]</paragraph></blockQuote>' +
  163. '<paragraph>x</paragraph>'
  164. );
  165. viewDocument.fire( 'enter', data );
  166. sinon.assert.calledOnce( scrollSpy );
  167. sinon.assert.callOrder( execSpy, scrollSpy );
  168. } );
  169. } );
  170. describe( 'backspace key support', () => {
  171. function fakeEventData() {
  172. return {
  173. preventDefault: sinon.spy(),
  174. direction: 'backward',
  175. unit: 'character'
  176. };
  177. }
  178. it( 'merges paragraph into paragraph in the quote', () => {
  179. const data = fakeEventData();
  180. setModelData( model,
  181. '<blockQuote><paragraph>a</paragraph><paragraph>b</paragraph></blockQuote>' +
  182. '<paragraph>[]c</paragraph>' +
  183. '<paragraph>d</paragraph>'
  184. );
  185. viewDocument.fire( 'delete', data );
  186. expect( getModelData( model ) ).to.equal(
  187. '<blockQuote><paragraph>a</paragraph><paragraph>b[]c</paragraph></blockQuote>' +
  188. '<paragraph>d</paragraph>'
  189. );
  190. } );
  191. it( 'merges paragraph from a quote into a paragraph before quote', () => {
  192. const data = fakeEventData();
  193. setModelData( model,
  194. '<paragraph>x</paragraph>' +
  195. '<blockQuote><paragraph>[]a</paragraph><paragraph>b</paragraph></blockQuote>' +
  196. '<paragraph>y</paragraph>'
  197. );
  198. viewDocument.fire( 'delete', data );
  199. expect( getModelData( model ) ).to.equal(
  200. '<paragraph>x[]a</paragraph>' +
  201. '<blockQuote><paragraph>b</paragraph></blockQuote>' +
  202. '<paragraph>y</paragraph>'
  203. );
  204. } );
  205. it( 'merges two quotes', () => {
  206. const data = fakeEventData();
  207. setModelData( model,
  208. '<paragraph>x</paragraph>' +
  209. '<blockQuote><paragraph>a</paragraph><paragraph>b</paragraph></blockQuote>' +
  210. '<blockQuote><paragraph>[]c</paragraph><paragraph>d</paragraph></blockQuote>' +
  211. '<paragraph>y</paragraph>'
  212. );
  213. viewDocument.fire( 'delete', data );
  214. expect( getModelData( model ) ).to.equal(
  215. '<paragraph>x</paragraph>' +
  216. '<blockQuote><paragraph>a</paragraph><paragraph>b[]c</paragraph><paragraph>d</paragraph></blockQuote>' +
  217. '<paragraph>y</paragraph>'
  218. );
  219. } );
  220. it( 'removes empty quote when merging into another quote', () => {
  221. const data = fakeEventData();
  222. setModelData( model,
  223. '<paragraph>x</paragraph>' +
  224. '<blockQuote><paragraph>a</paragraph></blockQuote>' +
  225. '<blockQuote><paragraph>[]</paragraph></blockQuote>' +
  226. '<paragraph>y</paragraph>'
  227. );
  228. viewDocument.fire( 'delete', data );
  229. expect( getModelData( model ) ).to.equal(
  230. '<paragraph>x</paragraph>' +
  231. '<blockQuote><paragraph>a[]</paragraph></blockQuote>' +
  232. '<paragraph>y</paragraph>'
  233. );
  234. } );
  235. it( 'removes empty quote when merging into a paragraph', () => {
  236. const data = fakeEventData();
  237. setModelData( model,
  238. '<paragraph>x</paragraph>' +
  239. '<blockQuote><paragraph>[]</paragraph></blockQuote>' +
  240. '<paragraph>y</paragraph>'
  241. );
  242. viewDocument.fire( 'delete', data );
  243. expect( getModelData( model ) ).to.equal(
  244. '<paragraph>x[]</paragraph>' +
  245. '<paragraph>y</paragraph>'
  246. );
  247. } );
  248. } );
  249. // Historically, due to problems with schema, images were not quotable.
  250. // These tests were left here to confirm that after schema was fixed, images are properly quotable.
  251. describe( 'compatibility with images', () => {
  252. it( 'quotes a simple image', () => {
  253. const element = document.createElement( 'div' );
  254. document.body.appendChild( element );
  255. // We can't load ImageCaption in this test because it adds <caption> to all images automatically.
  256. return ClassicTestEditor
  257. .create( element, {
  258. plugins: [ BlockQuote, Paragraph, Image ]
  259. } )
  260. .then( editor => {
  261. setModelData( editor.model,
  262. '<paragraph>fo[o</paragraph>' +
  263. '<image src="foo.png"></image>' +
  264. '<paragraph>b]ar</paragraph>'
  265. );
  266. editor.execute( 'blockQuote' );
  267. expect( getModelData( editor.model ) ).to.equal(
  268. '<blockQuote>' +
  269. '<paragraph>fo[o</paragraph>' +
  270. '<image src="foo.png"></image>' +
  271. '<paragraph>b]ar</paragraph>' +
  272. '</blockQuote>'
  273. );
  274. element.remove();
  275. return editor.destroy();
  276. } );
  277. } );
  278. it( 'quotes an image with caption', () => {
  279. setModelData( model,
  280. '<paragraph>fo[o</paragraph>' +
  281. '<image src="foo.png">' +
  282. '<caption>xxx</caption>' +
  283. '</image>' +
  284. '<paragraph>b]ar</paragraph>'
  285. );
  286. editor.execute( 'blockQuote' );
  287. expect( getModelData( model ) ).to.equal(
  288. '<blockQuote>' +
  289. '<paragraph>fo[o</paragraph>' +
  290. '<image src="foo.png">' +
  291. '<caption>xxx</caption>' +
  292. '</image>' +
  293. '<paragraph>b]ar</paragraph>' +
  294. '</blockQuote>'
  295. );
  296. } );
  297. it( 'adds an image to an existing quote', () => {
  298. setModelData( model,
  299. '<paragraph>fo[o</paragraph>' +
  300. '<image src="foo.png">' +
  301. '<caption>xxx</caption>' +
  302. '</image>' +
  303. '<blockQuote><paragraph>b]ar</paragraph></blockQuote>'
  304. );
  305. editor.execute( 'blockQuote' );
  306. // Selection incorrectly trimmed.
  307. expect( getModelData( model ) ).to.equal(
  308. '<blockQuote>' +
  309. '<paragraph>foo</paragraph>' +
  310. '<image src="foo.png">' +
  311. '<caption>xxx</caption>' +
  312. '</image>' +
  313. '<paragraph>[b]ar</paragraph>' +
  314. '</blockQuote>'
  315. );
  316. } );
  317. it( 'wraps paragraph+image', () => {
  318. setModelData( model,
  319. '<paragraph>[foo</paragraph><image><caption>foo</caption></image>]'
  320. );
  321. editor.execute( 'blockQuote' );
  322. expect( getModelData( model ) ).to.equal(
  323. '<blockQuote><paragraph>[foo</paragraph><image><caption>foo</caption></image>]</blockQuote>'
  324. );
  325. } );
  326. it( 'unwraps paragraph+image', () => {
  327. setModelData( model,
  328. '<blockQuote><paragraph>[foo</paragraph><image><caption>foo</caption></image>]</blockQuote>'
  329. );
  330. editor.execute( 'blockQuote' );
  331. expect( getModelData( model ) ).to.equal(
  332. '<paragraph>[foo</paragraph><image><caption>foo</caption></image>]'
  333. );
  334. } );
  335. it( 'wraps image+paragraph', () => {
  336. setModelData( model,
  337. '[<image><caption>foo</caption></image><paragraph>foo]</paragraph>'
  338. );
  339. editor.execute( 'blockQuote' );
  340. expect( getModelData( model ) ).to.equal(
  341. '<blockQuote>[<image><caption>foo</caption></image><paragraph>foo]</paragraph></blockQuote>'
  342. );
  343. } );
  344. it( 'unwraps image+paragraph', () => {
  345. setModelData( model,
  346. '[<image><caption>foo</caption></image><paragraph>foo]</paragraph>'
  347. );
  348. editor.execute( 'blockQuote' );
  349. expect( getModelData( model ) ).to.equal(
  350. '<blockQuote>[<image><caption>foo</caption></image><paragraph>foo]</paragraph></blockQuote>'
  351. );
  352. } );
  353. } );
  354. // When blockQuote with a paragraph was pasted into a list item, the item contained the paragraph. It was invalid.
  355. // There is a test which checks whether blockQuote will split the list items instead of merging with.
  356. describe( 'compatibility with lists', () => {
  357. it( 'does not merge the paragraph with list item', () => {
  358. setModelData( model, '<listItem listIndent="0" listType="bulleted">fo[]o</listItem>' );
  359. const df = parseModel(
  360. '<blockQuote><paragraph>xxx</paragraph></blockQuote><heading1>yyy</heading1>',
  361. model.schema
  362. );
  363. model.insertContent( df, model.document.selection );
  364. expect( getModelData( model ) ).to.equal(
  365. '<listItem listIndent="0" listType="bulleted">fo</listItem>' +
  366. '<blockQuote>' +
  367. '<paragraph>xxx</paragraph>' +
  368. '</blockQuote>' +
  369. '<heading1>yyy[]o</heading1>'
  370. );
  371. } );
  372. } );
  373. describe( 'compatibility with tables', () => {
  374. it( 'wraps whole table', () => {
  375. setModelData( model, '[<table><tableRow><tableCell><paragraph>foo</paragraph></tableCell></tableRow></table>]' );
  376. editor.execute( 'blockQuote' );
  377. expect( getModelData( model ) ).to.equal(
  378. '<blockQuote>[<table><tableRow><tableCell><paragraph>foo</paragraph></tableCell></tableRow></table>]</blockQuote>'
  379. );
  380. } );
  381. it( 'unwraps whole table', () => {
  382. setModelData(
  383. model,
  384. '<blockQuote>[<table><tableRow><tableCell><paragraph>foo</paragraph></tableCell></tableRow></table>]</blockQuote>'
  385. );
  386. editor.execute( 'blockQuote' );
  387. expect( getModelData( model ) ).to.equal(
  388. '[<table><tableRow><tableCell><paragraph>foo</paragraph></tableCell></tableRow></table>]'
  389. );
  390. } );
  391. it( 'wraps paragraph in table cell', () => {
  392. setModelData( model, '<table><tableRow><tableCell><paragraph>[]foo</paragraph></tableCell></tableRow></table>' );
  393. editor.execute( 'blockQuote' );
  394. expect( getModelData( model ) ).to.equal(
  395. '<table><tableRow><tableCell><blockQuote><paragraph>[]foo</paragraph></blockQuote></tableCell></tableRow></table>'
  396. );
  397. } );
  398. it( 'unwraps paragraph in table cell', () => {
  399. setModelData(
  400. model,
  401. '<table><tableRow><tableCell><blockQuote><paragraph>[]foo</paragraph></blockQuote></tableCell></tableRow></table>'
  402. );
  403. editor.execute( 'blockQuote' );
  404. expect( getModelData( model ) ).to.equal(
  405. '<table><tableRow><tableCell><paragraph>[]foo</paragraph></tableCell></tableRow></table>'
  406. );
  407. } );
  408. it( 'wraps image in table cell', () => {
  409. setModelData( model,
  410. '<table>' +
  411. '<tableRow>' +
  412. '<tableCell>[<image><caption>foo</caption></image>]</tableCell>' +
  413. ' </tableRow>' +
  414. '</table>'
  415. );
  416. editor.execute( 'blockQuote' );
  417. expect( getModelData( model ) ).to.equal(
  418. '<table>' +
  419. '<tableRow>' +
  420. '<tableCell><blockQuote>[<image><caption>foo</caption></image>]</blockQuote></tableCell>' +
  421. '</tableRow>' +
  422. '</table>'
  423. );
  424. } );
  425. it( 'unwraps image in table cell', () => {
  426. setModelData( model,
  427. '<table>' +
  428. '<tableRow>' +
  429. '<tableCell><blockQuote>[<image><caption>foo</caption></image>]</blockQuote></tableCell>' +
  430. '</tableRow>' +
  431. '</table>'
  432. );
  433. editor.execute( 'blockQuote' );
  434. expect( getModelData( model ) ).to.equal(
  435. '<table>' +
  436. '<tableRow>' +
  437. '<tableCell>[<image><caption>foo</caption></image>]</tableCell>' +
  438. '</tableRow>' +
  439. '</table>'
  440. );
  441. } );
  442. it( 'wraps paragraph+image in table cell', () => {
  443. setModelData( model,
  444. '<table>' +
  445. '<tableRow>' +
  446. '<tableCell><paragraph>[foo</paragraph><image><caption>foo</caption></image>]</tableCell>' +
  447. '</tableRow>' +
  448. '</table>'
  449. );
  450. editor.execute( 'blockQuote' );
  451. expect( getModelData( model ) ).to.equal(
  452. '<table>' +
  453. '<tableRow>' +
  454. '<tableCell>' +
  455. '<blockQuote><paragraph>[foo</paragraph><image><caption>foo</caption></image>]</blockQuote>' +
  456. '</tableCell>' +
  457. '</tableRow>' +
  458. '</table>'
  459. );
  460. } );
  461. it( 'unwraps paragraph+image in table cell', () => {
  462. setModelData( model,
  463. '<table>' +
  464. '<tableRow>' +
  465. '<tableCell>' +
  466. '<blockQuote><paragraph>[foo</paragraph><image><caption>foo</caption></image>]</blockQuote>' +
  467. '</tableCell>' +
  468. '</tableRow>' +
  469. '</table>'
  470. );
  471. editor.execute( 'blockQuote' );
  472. expect( getModelData( model ) ).to.equal(
  473. '<table>' +
  474. '<tableRow>' +
  475. '<tableCell><paragraph>[foo</paragraph><image><caption>foo</caption></image>]</tableCell>' +
  476. '</tableRow>' +
  477. '</table>'
  478. );
  479. } );
  480. it.only( 'wraps image+paragraph in table cell', () => {
  481. setModelData( model,
  482. '<table>' +
  483. '<tableRow>' +
  484. '<tableCell>[<image><caption>foo</caption></image><paragraph>foo]</paragraph></tableCell>' +
  485. '</tableRow>' +
  486. '</table>'
  487. );
  488. editor.execute( 'blockQuote' );
  489. expect( getModelData( model ) ).to.equal(
  490. '<table>' +
  491. '<tableRow>' +
  492. '<tableCell>' +
  493. '<blockQuote>[<image><caption>foo</caption></image><paragraph>foo]</paragraph></blockQuote>' +
  494. '</tableCell>' +
  495. '</tableRow>' +
  496. '</table>'
  497. );
  498. } );
  499. it.only( 'unwraps image+paragraph in table cell', () => {
  500. setModelData( model,
  501. '<table>' +
  502. '<tableRow>' +
  503. '<tableCell>[<image><caption>foo</caption></image><paragraph>foo]</paragraph></tableCell>' +
  504. '</tableRow>' +
  505. '</table>'
  506. );
  507. editor.execute( 'blockQuote' );
  508. expect( getModelData( model ) ).to.equal(
  509. '<table>' +
  510. '<tableRow>' +
  511. '<tableCell>' +
  512. '<blockQuote>[<image><caption>foo</caption></image><paragraph>foo]</paragraph></blockQuote>' +
  513. '</tableCell>' +
  514. '</tableRow>' +
  515. '</table>'
  516. );
  517. } );
  518. } );
  519. } );