upcasttable.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. /**
  2. * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
  6. import { upcastElementToElement } from '@ckeditor/ckeditor5-engine/src/conversion/upcast-converters';
  7. import { getData as getModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
  8. import upcastTable from '../../src/converters/upcasttable';
  9. describe( 'upcastTable()', () => {
  10. let editor, model;
  11. beforeEach( () => {
  12. return VirtualTestEditor.create()
  13. .then( newEditor => {
  14. editor = newEditor;
  15. model = editor.model;
  16. const conversion = editor.conversion;
  17. const schema = model.schema;
  18. schema.register( 'table', {
  19. allowWhere: '$block',
  20. allowAttributes: [ 'headingRows', 'headingColumns' ],
  21. isBlock: true,
  22. isObject: true
  23. } );
  24. schema.register( 'tableRow', {
  25. allowIn: 'table',
  26. allowAttributes: [],
  27. isBlock: true,
  28. isLimit: true
  29. } );
  30. schema.register( 'tableCell', {
  31. allowIn: 'tableRow',
  32. allowContentOf: '$block',
  33. allowAttributes: [ 'colspan', 'rowspan' ],
  34. isBlock: true,
  35. isLimit: true
  36. } );
  37. conversion.for( 'upcast' ).add( upcastTable() );
  38. // Table row upcast only since downcast conversion is done in `downcastTable()`.
  39. conversion.for( 'upcast' ).add( upcastElementToElement( { model: 'tableRow', view: 'tr' } ) );
  40. // Table cell conversion.
  41. conversion.for( 'upcast' ).add( upcastElementToElement( { model: 'tableCell', view: 'td' } ) );
  42. conversion.for( 'upcast' ).add( upcastElementToElement( { model: 'tableCell', view: 'th' } ) );
  43. conversion.attributeToAttribute( { model: 'colspan', view: 'colspan' } );
  44. conversion.attributeToAttribute( { model: 'rowspan', view: 'rowspan' } );
  45. // Since this part of test tests only view->model conversion editing pipeline is not necessary
  46. // so defining model->view converters won't be necessary.
  47. editor.editing.destroy();
  48. } );
  49. } );
  50. function expectModel( data ) {
  51. expect( getModelData( model, { withoutSelection: true } ) ).to.equal( data );
  52. }
  53. it( 'should create table model from table without thead', () => {
  54. editor.setData(
  55. '<table>' +
  56. '<tr><td>1</td></tr>' +
  57. '</table>'
  58. );
  59. expectModel(
  60. '<table>' +
  61. '<tableRow><tableCell>1</tableCell></tableRow>' +
  62. '</table>'
  63. );
  64. } );
  65. it( 'should create table model from table with one thead with one row', () => {
  66. editor.setData(
  67. '<table>' +
  68. '<thead><tr><td>1</td></tr></thead>' +
  69. '</table>'
  70. );
  71. expectModel(
  72. '<table headingRows="1">' +
  73. '<tableRow><tableCell>1</tableCell></tableRow>' +
  74. '</table>'
  75. );
  76. } );
  77. it( 'should create table model from table with one thead with more then on row', () => {
  78. editor.setData(
  79. '<table>' +
  80. '<thead>' +
  81. '<tr><td>1</td></tr>' +
  82. '<tr><td>2</td></tr>' +
  83. '<tr><td>3</td></tr>' +
  84. '</thead>' +
  85. '</table>'
  86. );
  87. expectModel(
  88. '<table headingRows="3">' +
  89. '<tableRow><tableCell>1</tableCell></tableRow>' +
  90. '<tableRow><tableCell>2</tableCell></tableRow>' +
  91. '<tableRow><tableCell>3</tableCell></tableRow>' +
  92. '</table>'
  93. );
  94. } );
  95. it( 'should create table model from table with two theads with one row', () => {
  96. editor.setData(
  97. '<table>' +
  98. '<thead><tr><td>1</td></tr></thead>' +
  99. '<tbody><tr><td>2</td></tr></tbody>' +
  100. '<thead><tr><td>3</td></tr></thead>' +
  101. '</table>'
  102. );
  103. expectModel(
  104. '<table headingRows="1">' +
  105. '<tableRow><tableCell>1</tableCell></tableRow>' +
  106. '<tableRow><tableCell>2</tableCell></tableRow>' +
  107. '<tableRow><tableCell>3</tableCell></tableRow>' +
  108. '</table>'
  109. );
  110. } );
  111. it( 'should create table model from table with thead after the tbody', () => {
  112. editor.setData(
  113. '<table>' +
  114. '<tbody><tr><td>2</td></tr></tbody>' +
  115. '<thead><tr><td>1</td></tr></thead>' +
  116. '</table>'
  117. );
  118. expectModel(
  119. '<table headingRows="1">' +
  120. '<tableRow><tableCell>1</tableCell></tableRow>' +
  121. '<tableRow><tableCell>2</tableCell></tableRow>' +
  122. '</table>'
  123. );
  124. } );
  125. it( 'should create table model from table with one tfoot with one row', () => {
  126. editor.setData(
  127. '<table>' +
  128. '<tfoot><tr><td>1</td></tr></tfoot>' +
  129. '</table>'
  130. );
  131. expectModel(
  132. '<table>' +
  133. '<tableRow><tableCell>1</tableCell></tableRow>' +
  134. '</table>'
  135. );
  136. } );
  137. it( 'should create valid table model from empty table', () => {
  138. editor.setData(
  139. '<table>' +
  140. '</table>'
  141. );
  142. expectModel(
  143. '<table><tableRow><tableCell></tableCell></tableRow></table>'
  144. );
  145. } );
  146. it( 'should skip unknown table children', () => {
  147. editor.setData(
  148. '<table>' +
  149. '<caption>foo</caption>' +
  150. '<tr><td>bar</td></tr>' +
  151. '</table>'
  152. );
  153. expectModel(
  154. '<table><tableRow><tableCell>bar</tableCell></tableRow></table>'
  155. );
  156. } );
  157. it( 'should create table model from some broken table', () => {
  158. editor.setData(
  159. '<table><td><p>foo</p></td></table>'
  160. );
  161. expectModel(
  162. '<table><tableRow><tableCell>foo</tableCell></tableRow></table>'
  163. );
  164. } );
  165. it( 'should fix if inside other blocks', () => {
  166. // Using <div> instead of <p> as it breaks on Edge.
  167. editor.model.schema.register( 'div', {
  168. inheritAllFrom: '$block'
  169. } );
  170. editor.conversion.for( 'upcast' ).add( upcastElementToElement( { model: 'div', view: 'div' } ) );
  171. editor.setData(
  172. '<div>foo' +
  173. '<table>' +
  174. '<tbody><tr><td>2</td></tr></tbody>' +
  175. '<thead><tr><td>1</td></tr></thead>' +
  176. '</table>' +
  177. 'bar</div>'
  178. );
  179. expectModel(
  180. '<div>foo</div>' +
  181. '<table headingRows="1">' +
  182. '<tableRow><tableCell>1</tableCell></tableRow>' +
  183. '<tableRow><tableCell>2</tableCell></tableRow>' +
  184. '</table>' +
  185. '<div>bar</div>'
  186. );
  187. } );
  188. it( 'should be possible to overwrite table conversion', () => {
  189. editor.model.schema.register( 'fooTable', {
  190. allowWhere: '$block',
  191. allowAttributes: [ 'headingRows' ],
  192. isBlock: true,
  193. isObject: true
  194. } );
  195. editor.conversion.elementToElement( { model: 'fooTable', view: 'table', priority: 'high' } );
  196. editor.setData(
  197. '<table>' +
  198. '<thead><tr><td>foo</td></tr></thead>' +
  199. '</table>'
  200. );
  201. expectModel(
  202. '<fooTable></fooTable>'
  203. );
  204. } );
  205. describe( 'headingColumns', () => {
  206. it( 'should properly calculate heading columns', () => {
  207. editor.setData(
  208. '<table>' +
  209. '<tbody>' +
  210. // This row starts with 1 th (3 total).
  211. '<tr><th>21</th><td>22</td><th>23</th><th>24</th></tr>' +
  212. // This row starts with 2 th (2 total). This one has max number of heading columns: 2.
  213. '<tr><th>31</th><th>32</th><td>33</td><td>34</td></tr>' +
  214. // This row starts with 1 th (1 total).
  215. '<tr><th>41</th><td>42</td><td>43</td><td>44</td></tr>' +
  216. // This row starts with 0 th (3 total).
  217. '<tr><td>51</td><th>52</th><th>53</th><th>54</th></tr>' +
  218. '</tbody>' +
  219. '<thead>' +
  220. // This row has 4 ths but it is a thead.
  221. '<tr><th>11</th><th>12</th><th>13</th><th>14</th></tr>' +
  222. '</thead>' +
  223. '</table>'
  224. );
  225. expectModel(
  226. '<table headingColumns="2" headingRows="1">' +
  227. '<tableRow>' +
  228. '<tableCell>11</tableCell><tableCell>12</tableCell><tableCell>13</tableCell><tableCell>14</tableCell>' +
  229. '</tableRow>' +
  230. '<tableRow>' +
  231. '<tableCell>21</tableCell><tableCell>22</tableCell><tableCell>23</tableCell><tableCell>24</tableCell>' +
  232. '</tableRow>' +
  233. '<tableRow>' +
  234. '<tableCell>31</tableCell><tableCell>32</tableCell><tableCell>33</tableCell><tableCell>34</tableCell>' +
  235. '</tableRow>' +
  236. '<tableRow>' +
  237. '<tableCell>41</tableCell><tableCell>42</tableCell><tableCell>43</tableCell><tableCell>44</tableCell>' +
  238. '</tableRow>' +
  239. '<tableRow>' +
  240. '<tableCell>51</tableCell><tableCell>52</tableCell><tableCell>53</tableCell><tableCell>54</tableCell>' +
  241. '</tableRow>' +
  242. '</table>'
  243. );
  244. } );
  245. it( 'should calculate heading columns of cells with colspan', () => {
  246. editor.setData(
  247. '<table>' +
  248. '<tbody>' +
  249. // This row has colspan of 3 so it should be the whole table should have 3 heading columns.
  250. '<tr><th>21</th><th>22</th><td>23</td><td>24</td></tr>' +
  251. '<tr><th colspan="2">31</th><th>33</th><td>34</td></tr>' +
  252. '</tbody>' +
  253. '<thead>' +
  254. // This row has 4 ths but it is a thead.
  255. '<tr><th>11</th><th>12</th><th>13</th><th>14</th></tr>' +
  256. '</thead>' +
  257. '</table>'
  258. );
  259. expectModel(
  260. '<table headingColumns="3" headingRows="1">' +
  261. '<tableRow>' +
  262. '<tableCell>11</tableCell><tableCell>12</tableCell><tableCell>13</tableCell><tableCell>14</tableCell>' +
  263. '</tableRow>' +
  264. '<tableRow>' +
  265. '<tableCell>21</tableCell><tableCell>22</tableCell><tableCell>23</tableCell><tableCell>24</tableCell>' +
  266. '</tableRow>' +
  267. '<tableRow>' +
  268. '<tableCell colspan="2">31</tableCell><tableCell>33</tableCell><tableCell>34</tableCell>' +
  269. '</tableRow>' +
  270. '</table>'
  271. );
  272. } );
  273. } );
  274. } );