tableeditor_test.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. // Copyright 2009 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. goog.provide('goog.editor.plugins.TableEditorTest');
  15. goog.setTestOnly('goog.editor.plugins.TableEditorTest');
  16. goog.require('goog.dom');
  17. goog.require('goog.dom.Range');
  18. goog.require('goog.dom.TagName');
  19. goog.require('goog.editor.plugins.TableEditor');
  20. goog.require('goog.object');
  21. goog.require('goog.string');
  22. goog.require('goog.testing.ExpectedFailures');
  23. goog.require('goog.testing.JsUnitException');
  24. goog.require('goog.testing.TestCase');
  25. goog.require('goog.testing.editor.FieldMock');
  26. goog.require('goog.testing.editor.TestHelper');
  27. goog.require('goog.testing.jsunit');
  28. goog.require('goog.userAgent');
  29. var field;
  30. var plugin;
  31. var fieldMock;
  32. var expectedFailures;
  33. var testHelper;
  34. function setUpPage() {
  35. field = goog.dom.getElement('field');
  36. expectedFailures = new goog.testing.ExpectedFailures();
  37. }
  38. function setUp() {
  39. // TODO(b/25875505): Fix unreported assertions (go/failonunreportedasserts).
  40. goog.testing.TestCase.getActiveTestCase().failOnUnreportedAsserts = false;
  41. testHelper = new goog.testing.editor.TestHelper(goog.dom.getElement('field'));
  42. testHelper.setUpEditableElement();
  43. field.focus();
  44. plugin = new goog.editor.plugins.TableEditor();
  45. fieldMock = new goog.testing.editor.FieldMock();
  46. plugin.registerFieldObject(fieldMock);
  47. if (goog.userAgent.IE &&
  48. (goog.userAgent.compare(goog.userAgent.VERSION, '7.0') >= 0)) {
  49. goog.testing.TestCase.protectedTimeout_ = window.setTimeout;
  50. }
  51. }
  52. function tearDown() {
  53. testHelper.tearDownEditableElement();
  54. expectedFailures.handleTearDown();
  55. }
  56. function testEnable() {
  57. fieldMock.$replay();
  58. plugin.enable(fieldMock);
  59. assertTrue('Plugin should be enabled', plugin.isEnabled(fieldMock));
  60. if (goog.userAgent.GECKO) {
  61. // This code path is executed only for GECKO browsers but we can't
  62. // verify it because of a GECKO bug while reading the value of the
  63. // command "enableObjectResizing".
  64. // See https://bugzilla.mozilla.org/show_bug.cgi?id=506368
  65. expectedFailures.expectFailureFor(goog.userAgent.GECKO);
  66. try {
  67. var doc = plugin.getFieldDomHelper().getDocument();
  68. assertTrue(
  69. 'Object resizing should be enabled',
  70. doc.queryCommandValue('enableObjectResizing'));
  71. } catch (e) {
  72. // We need to marshal our exception in order for it to be handled
  73. // properly.
  74. expectedFailures.handleException(new goog.testing.JsUnitException(e));
  75. }
  76. }
  77. fieldMock.$verify();
  78. }
  79. function testIsSupportedCommand() {
  80. goog.object.forEach(
  81. goog.editor.plugins.TableEditor.COMMAND, function(command) {
  82. assertTrue(
  83. goog.string.subs('Plugin should support %s', command),
  84. plugin.isSupportedCommand(command));
  85. });
  86. assertFalse(
  87. 'Plugin shouldn\'t support a bogus command',
  88. plugin.isSupportedCommand('+fable'));
  89. }
  90. function testCreateTable() {
  91. fieldMock.$replay();
  92. createTableAndSelectCell();
  93. var table = plugin.getCurrentTable_();
  94. assertNotNull('Table should not be null', table);
  95. assertEquals(
  96. 'Table should have the default number of rows', 2, table.rows.length);
  97. assertEquals(
  98. 'Table should have the default number of cells', 8, getCellCount(table));
  99. fieldMock.$verify();
  100. }
  101. function testInsertRowBefore() {
  102. fieldMock.$replay();
  103. createTableAndSelectCell();
  104. var table = plugin.getCurrentTable_();
  105. var selectedRow = fieldMock.getRange().getContainerElement().parentNode;
  106. assertNull(
  107. 'Selected row shouldn\'t have a previous sibling',
  108. selectedRow.previousSibling);
  109. assertEquals('Table should have two rows', 2, table.rows.length);
  110. plugin.execCommandInternal(
  111. goog.editor.plugins.TableEditor.COMMAND.INSERT_ROW_BEFORE);
  112. assertEquals('A row should have been inserted', 3, table.rows.length);
  113. // Assert that we inserted a row above the currently selected row.
  114. assertNotNull(
  115. 'Selected row should have a previous sibling',
  116. selectedRow.previousSibling);
  117. fieldMock.$verify();
  118. }
  119. function testInsertRowAfter() {
  120. fieldMock.$replay();
  121. createTableAndSelectCell({width: 2, height: 1});
  122. var selectedRow = fieldMock.getRange().getContainerElement().parentNode;
  123. var table = plugin.getCurrentTable_();
  124. assertEquals('Table should have one row', 1, table.rows.length);
  125. assertNull(
  126. 'Selected row shouldn\'t have a next sibling', selectedRow.nextSibling);
  127. plugin.execCommandInternal(
  128. goog.editor.plugins.TableEditor.COMMAND.INSERT_ROW_AFTER);
  129. assertEquals('A row should have been inserted', 2, table.rows.length);
  130. // Assert that we inserted a row after the currently selected row.
  131. assertNotNull(
  132. 'Selected row should have a next sibling', selectedRow.nextSibling);
  133. fieldMock.$verify();
  134. }
  135. function testInsertColumnBefore() {
  136. fieldMock.$replay();
  137. createTableAndSelectCell({width: 1, height: 1});
  138. var table = plugin.getCurrentTable_();
  139. var selectedCell = fieldMock.getRange().getContainerElement();
  140. assertEquals('Table should have one cell', 1, getCellCount(table));
  141. assertNull(
  142. 'Selected cell shouldn\'t have a previous sibling',
  143. selectedCell.previousSibling);
  144. plugin.execCommandInternal(
  145. goog.editor.plugins.TableEditor.COMMAND.INSERT_COLUMN_BEFORE);
  146. assertEquals('A cell should have been inserted', 2, getCellCount(table));
  147. assertNotNull(
  148. 'Selected cell should have a previous sibling',
  149. selectedCell.previousSibling);
  150. fieldMock.$verify();
  151. }
  152. function testInsertColumnAfter() {
  153. fieldMock.$replay();
  154. createTableAndSelectCell({width: 1, height: 1});
  155. var table = plugin.getCurrentTable_();
  156. var selectedCell = fieldMock.getRange().getContainerElement();
  157. assertEquals('Table should have one cell', 1, getCellCount(table));
  158. assertNull(
  159. 'Selected cell shouldn\'t have a next sibling', selectedCell.nextSibling);
  160. plugin.execCommandInternal(
  161. goog.editor.plugins.TableEditor.COMMAND.INSERT_COLUMN_AFTER);
  162. assertEquals('A cell should have been inserted', 2, getCellCount(table));
  163. assertNotNull(
  164. 'Selected cell should have a next sibling', selectedCell.nextSibling);
  165. fieldMock.$verify();
  166. }
  167. function testRemoveRows() {
  168. fieldMock.$replay();
  169. createTableAndSelectCell({width: 1, height: 2});
  170. var table = plugin.getCurrentTable_();
  171. var selectedCell = fieldMock.getRange().getContainerElement();
  172. selectedCell.id = 'selected';
  173. assertEquals('Table should have two rows', 2, table.rows.length);
  174. plugin.execCommandInternal(
  175. goog.editor.plugins.TableEditor.COMMAND.REMOVE_ROWS);
  176. assertEquals('A row should have been removed', 1, table.rows.length);
  177. assertNull(
  178. 'The correct row should have been removed',
  179. goog.dom.getElement('selected'));
  180. // Verify that the table is removed if we don't have any rows.
  181. plugin.execCommandInternal(
  182. goog.editor.plugins.TableEditor.COMMAND.REMOVE_ROWS);
  183. assertEquals(
  184. 'The table should have been removed', 0,
  185. goog.dom.getElementsByTagName(goog.dom.TagName.TABLE, field).length);
  186. fieldMock.$verify();
  187. }
  188. function testRemoveColumns() {
  189. fieldMock.$replay();
  190. createTableAndSelectCell({width: 2, height: 1});
  191. var table = plugin.getCurrentTable_();
  192. var selectedCell = fieldMock.getRange().getContainerElement();
  193. selectedCell.id = 'selected';
  194. assertEquals('Table should have two cells', 2, getCellCount(table));
  195. plugin.execCommandInternal(
  196. goog.editor.plugins.TableEditor.COMMAND.REMOVE_COLUMNS);
  197. assertEquals('A cell should have been removed', 1, getCellCount(table));
  198. assertNull(
  199. 'The correct cell should have been removed',
  200. goog.dom.getElement('selected'));
  201. // Verify that the table is removed if we don't have any columns.
  202. plugin.execCommandInternal(
  203. goog.editor.plugins.TableEditor.COMMAND.REMOVE_COLUMNS);
  204. assertEquals(
  205. 'The table should have been removed', 0,
  206. goog.dom.getElementsByTagName(goog.dom.TagName.TABLE, field).length);
  207. fieldMock.$verify();
  208. }
  209. function testSplitCell() {
  210. fieldMock.$replay();
  211. createTableAndSelectCell({width: 1, height: 1});
  212. var table = plugin.getCurrentTable_();
  213. var selectedCell = fieldMock.getRange().getContainerElement();
  214. // Splitting is only supported if we set these attributes.
  215. selectedCell.rowSpan = '1';
  216. selectedCell.colSpan = '2';
  217. goog.dom.setTextContent(selectedCell, 'foo');
  218. goog.dom.Range.createFromNodeContents(selectedCell).select();
  219. assertEquals('Table should have one cell', 1, getCellCount(table));
  220. plugin.execCommandInternal(
  221. goog.editor.plugins.TableEditor.COMMAND.SPLIT_CELL);
  222. assertEquals('The cell should have been split', 2, getCellCount(table));
  223. assertEquals(
  224. 'The cell content should be intact', 'foo', selectedCell.innerHTML);
  225. assertNotNull(
  226. 'The new cell should be inserted before', selectedCell.previousSibling);
  227. fieldMock.$verify();
  228. }
  229. function testMergeCells() {
  230. fieldMock.$replay();
  231. createTableAndSelectCell({width: 2, height: 1});
  232. var table = plugin.getCurrentTable_();
  233. var selectedCell = fieldMock.getRange().getContainerElement();
  234. goog.dom.setTextContent(selectedCell, 'foo');
  235. goog.dom.setTextContent(selectedCell.nextSibling, 'bar');
  236. var range = goog.dom.Range.createFromNodeContents(
  237. goog.dom.getElementsByTagName(goog.dom.TagName.TR, table)[0]);
  238. range.select();
  239. plugin.execCommandInternal(
  240. goog.editor.plugins.TableEditor.COMMAND.MERGE_CELLS);
  241. expectedFailures.expectFailureFor(
  242. goog.userAgent.IE && goog.userAgent.isVersionOrHigher('8'));
  243. try {
  244. // In IE8, even after explicitly setting the range to span
  245. // multiple cells, the browser selection only contains the first TD
  246. // which causes the merge operation to fail.
  247. assertEquals('The cells should be merged', 1, getCellCount(table));
  248. assertEquals(
  249. 'The cell should have expected colspan', 2, selectedCell.colSpan);
  250. assertHTMLEquals(
  251. 'The content should be merged', 'foo bar', selectedCell.innerHTML);
  252. } catch (e) {
  253. expectedFailures.handleException(e);
  254. }
  255. fieldMock.$verify();
  256. }
  257. /**
  258. * Helper routine which returns the number of cells in the table.
  259. *
  260. * @param {Element} table The table in question.
  261. * @return {number} Number of cells.
  262. */
  263. function getCellCount(table) {
  264. return table.cells ? table.cells.length :
  265. table.rows[0].cells.length * table.rows.length;
  266. }
  267. /**
  268. * Helper method which creates a table and puts the cursor on the first TD.
  269. * In IE, the cursor isn't positioned in the first cell (TD) and we simulate
  270. * that behavior explicitly to be consistent across all browsers.
  271. *
  272. * @param {Object} op_tableProps Optional table properties.
  273. */
  274. function createTableAndSelectCell(opt_tableProps) {
  275. goog.dom.Range.createCaret(field, 1).select();
  276. plugin.execCommandInternal(
  277. goog.editor.plugins.TableEditor.COMMAND.TABLE, opt_tableProps);
  278. if (goog.userAgent.IE) {
  279. var range = goog.dom.Range.createFromNodeContents(
  280. goog.dom.getElementsByTagName(goog.dom.TagName.TD, field)[0]);
  281. range.select();
  282. }
  283. }