removeformatting_test.js 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091
  1. // Copyright 2008 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.RemoveFormattingTest');
  15. goog.setTestOnly('goog.editor.plugins.RemoveFormattingTest');
  16. goog.require('goog.dom');
  17. goog.require('goog.dom.Range');
  18. goog.require('goog.dom.TagName');
  19. goog.require('goog.editor.BrowserFeature');
  20. goog.require('goog.editor.plugins.RemoveFormatting');
  21. goog.require('goog.string');
  22. goog.require('goog.testing.ExpectedFailures');
  23. goog.require('goog.testing.dom');
  24. goog.require('goog.testing.editor.FieldMock');
  25. goog.require('goog.testing.editor.TestHelper');
  26. goog.require('goog.testing.jsunit');
  27. goog.require('goog.userAgent');
  28. goog.require('goog.userAgent.product');
  29. var SAVED_HTML;
  30. var FIELDMOCK;
  31. var FORMATTER;
  32. var testHelper;
  33. var WEBKIT_BEFORE_CHROME_8;
  34. var WEBKIT_AFTER_CHROME_16;
  35. var WEBKIT_AFTER_CHROME_21;
  36. var insertImageBoldGarbage = '';
  37. var insertImageFontGarbage = '';
  38. var controlHtml;
  39. var controlCleanHtml;
  40. var expectedFailures;
  41. function setUpPage() {
  42. WEBKIT_BEFORE_CHROME_8 =
  43. goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('534.10');
  44. WEBKIT_AFTER_CHROME_16 =
  45. goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('535.7');
  46. WEBKIT_AFTER_CHROME_21 =
  47. goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('537.1');
  48. // On Chrome 16, execCommand('insertImage') inserts a garbage BR
  49. // after the image that we insert. We use this command to paste HTML
  50. // in-place, because it has better paragraph-preserving semantics.
  51. //
  52. // TODO(nicksantos): Figure out if there are better chrome APIs that we
  53. // should be using, or if insertImage should just be fixed.
  54. if (WEBKIT_AFTER_CHROME_21) {
  55. insertImageBoldGarbage = '<br>';
  56. insertImageFontGarbage = '<br>';
  57. } else if (WEBKIT_AFTER_CHROME_16) {
  58. insertImageBoldGarbage = '<b><br/></b>';
  59. insertImageFontGarbage = '<font size="1"><br/></font>';
  60. } else if (goog.userAgent.EDGE) {
  61. if (goog.userAgent.product.isVersion(14)) {
  62. insertImageFontGarbage = '<fontsize="-1"></fontsize="-1">';
  63. } else {
  64. insertImageFontGarbage =
  65. '<fontsize="-1"><font class="p" size="-1"></font></fontsize="-1">';
  66. }
  67. }
  68. // Extra html to add to test html to make sure removeformatting is actually
  69. // getting called when you're testing if it leaves certain styles alone
  70. // (instead of not even running at all due to some other bug). However, adding
  71. // this extra text into the node to be selected screws up IE.
  72. // (e.g. <a><img></a><b>t</b> --> <a></a><a><img></a>t )
  73. // TODO(user): Remove this special casing once http://b/3131117 is
  74. // fixed.
  75. controlHtml = goog.userAgent.IE ? '' : '<u>control</u>';
  76. controlCleanHtml = goog.userAgent.IE ? '' : 'control';
  77. if (goog.userAgent.EDGE) {
  78. controlCleanHtml = 'control<u></u>';
  79. }
  80. expectedFailures = new goog.testing.ExpectedFailures();
  81. }
  82. function setUp() {
  83. testHelper =
  84. new goog.testing.editor.TestHelper(document.getElementById('html'));
  85. testHelper.setUpEditableElement();
  86. FIELDMOCK = new goog.testing.editor.FieldMock();
  87. FIELDMOCK.getElement();
  88. FIELDMOCK.$anyTimes();
  89. FIELDMOCK.$returns(document.getElementById('html'));
  90. FORMATTER = new goog.editor.plugins.RemoveFormatting();
  91. FORMATTER.fieldObject = FIELDMOCK;
  92. FIELDMOCK.$replay();
  93. }
  94. function tearDown() {
  95. expectedFailures.handleTearDown();
  96. testHelper.tearDownEditableElement();
  97. }
  98. function setUpTableTests() {
  99. var div = document.getElementById('html');
  100. div.innerHTML = '<table><tr> <th> head1</th><th id= "outerTh">' +
  101. '<span id="emptyTh">head2</span></th> </tr><tr> <td> one </td> <td>' +
  102. 'two </td> </tr><tr><td> three</td><td id="outerTd"> ' +
  103. '<span id="emptyTd"><strong>four</strong></span></td></tr>' +
  104. '<tr id="outerTr"><td><span id="emptyTr"> five </span></td></tr>' +
  105. '<tr id="outerTr2"><td id="cell1"><b>seven</b></td><td id="cell2">' +
  106. '<u>eight</u><span id="cellspan2"> foo</span></td></tr></table>';
  107. }
  108. function testTableTagsAreNotRemoved() {
  109. setUpTableTests();
  110. var span;
  111. // TD
  112. span = document.getElementById('emptyTd');
  113. goog.dom.Range.createFromNodeContents(span).select();
  114. FORMATTER.removeFormatting_();
  115. var elem = document.getElementById('outerTd');
  116. assertTrue('TD should not be removed', !!elem);
  117. if (!goog.userAgent.WEBKIT && !goog.userAgent.EDGE) {
  118. // webkit seems to have an Apple-style-span
  119. assertEquals(
  120. 'TD should be clean', 'four', goog.string.trim(elem.innerHTML));
  121. }
  122. // TR
  123. span = document.getElementById('outerTr');
  124. goog.dom.Range.createFromNodeContents(span).select();
  125. FORMATTER.removeFormatting_();
  126. elem = document.getElementById('outerTr');
  127. assertTrue('TR should not be removed', !!elem);
  128. // TH
  129. span = document.getElementById('emptyTh');
  130. goog.dom.Range.createFromNodeContents(span).select();
  131. FORMATTER.removeFormatting_();
  132. elem = document.getElementById('outerTh');
  133. assertTrue('TH should not be removed', !!elem);
  134. if (!goog.userAgent.WEBKIT && !goog.userAgent.EDGE) {
  135. // webkit seems to have an Apple-style-span
  136. assertEquals('TH should be clean', 'head2', elem.innerHTML);
  137. }
  138. }
  139. /**
  140. * We select two cells from the table and then make sure that there is no
  141. * data loss and basic formatting is removed from each cell.
  142. */
  143. function testTableDataIsNotRemoved() {
  144. setUpTableTests();
  145. if (goog.userAgent.IE) {
  146. // IE returns an "unspecified error" which seems to be beyond
  147. // ExpectedFailures' ability to catch.
  148. return;
  149. }
  150. expectedFailures.expectFailureFor(
  151. goog.userAgent.WEBKIT || goog.userAgent.EDGE,
  152. 'The content moves out of the table in WebKit and Edge.');
  153. if (goog.userAgent.IE) {
  154. // Not used since we bail out early for IE, but this is there so that
  155. // developers can easily reproduce IE error.
  156. goog.dom.Range.createFromNodeContents(document.getElementById('outerTr2'))
  157. .select();
  158. } else {
  159. var selection = window.getSelection();
  160. if (selection.rangeCount > 0) selection.removeAllRanges();
  161. var range = document.createRange();
  162. range.selectNode(document.getElementById('cell1'));
  163. selection.addRange(range);
  164. range = document.createRange();
  165. range.selectNode(document.getElementById('cell2'));
  166. selection.addRange(range);
  167. }
  168. expectedFailures.run(function() {
  169. FORMATTER.removeFormatting_();
  170. span = document.getElementById('outerTr2');
  171. assertEquals(
  172. 'Table data should not be removed',
  173. '<td id="cell1">seven</td><td id="cell2">eight foo</td>',
  174. span.innerHTML);
  175. });
  176. }
  177. function testLinksAreNotRemoved() {
  178. expectedFailures.expectFailureFor(
  179. WEBKIT_BEFORE_CHROME_8,
  180. 'WebKit\'s removeFormatting command removes links.');
  181. var anchor;
  182. var div = document.getElementById('html');
  183. div.innerHTML = 'Foo<span id="link">Pre<a href="http://www.google.com">' +
  184. 'Outside Span<span style="font-size:15pt">Inside Span' +
  185. '</span></a></span>';
  186. anchor = document.getElementById('link');
  187. goog.dom.Range.createFromNodeContents(anchor).select();
  188. expectedFailures.run(function() {
  189. FORMATTER.removeFormatting_();
  190. assertHTMLEquals(
  191. 'link should not be removed',
  192. 'FooPre<a href="http://www.google.com/">Outside SpanInside Span</a>',
  193. div.innerHTML);
  194. });
  195. }
  196. /**
  197. * A short formatting removal function for use with the RemoveFormatting
  198. * plugin. Does enough that we can tell this function was run over the
  199. * document.
  200. * @param {string} text The HTML in from the document.
  201. * @return {string} The "cleaned" HTML out.
  202. */
  203. function replacementFormattingFunc(text) {
  204. // Really basic so that we can just see this is executing.
  205. return text.replace(/Foo/gi, 'Bar').replace(/<[\/]*span[^>]*>/gi, '');
  206. }
  207. function testAlternateRemoveFormattingFunction() {
  208. var div = document.getElementById('html');
  209. div.innerHTML = 'Start<span id="remFormat">Foo<pre>Bar</pre>Baz</span>';
  210. FORMATTER.setRemoveFormattingFunc(replacementFormattingFunc);
  211. var area = document.getElementById('remFormat');
  212. goog.dom.Range.createFromNodeContents(area).select();
  213. FORMATTER.removeFormatting_();
  214. // Webkit will change all tags to non-formatted ones anyway.
  215. // Make sure 'Foo' was changed to 'Bar'
  216. if (WEBKIT_BEFORE_CHROME_8) {
  217. assertHTMLEquals(
  218. 'regular cleaner should not have run', 'StartBar<br>Bar<br>Baz',
  219. div.innerHTML);
  220. } else {
  221. assertHTMLEquals(
  222. 'regular cleaner should not have run', 'StartBar<pre>Bar</pre>Baz',
  223. div.innerHTML);
  224. }
  225. }
  226. function testGetValueForNode() {
  227. // Override getValueForNode to keep bold tags.
  228. var oldGetValue =
  229. goog.editor.plugins.RemoveFormatting.prototype.getValueForNode;
  230. goog.editor.plugins.RemoveFormatting.prototype.getValueForNode = function(
  231. node) {
  232. if (node.nodeName == goog.dom.TagName.B) {
  233. return '<b>' + this.removeFormattingWorker_(node.innerHTML) + '</b>';
  234. }
  235. return null;
  236. };
  237. var html = FORMATTER.removeFormattingWorker_('<div>foo<b>bar</b></div>');
  238. assertHTMLEquals('B tags should remain', 'foo<b>bar</b>', html);
  239. // Override getValueForNode to throw out bold tags, and their contents.
  240. goog.editor.plugins.RemoveFormatting.prototype.getValueForNode = function(
  241. node) {
  242. if (node.nodeName == goog.dom.TagName.B) {
  243. return '';
  244. }
  245. return null;
  246. };
  247. html = FORMATTER.removeFormattingWorker_('<div>foo<b>bar</b></div>');
  248. assertHTMLEquals('B tag and its contents should be removed', 'foo', html);
  249. FIELDMOCK.$verify();
  250. goog.editor.plugins.RemoveFormatting.prototype.getValueForNode = oldGetValue;
  251. }
  252. function testRemoveFormattingAddsNoNbsps() {
  253. var div = document.getElementById('html');
  254. div.innerHTML = '"<span id="toStrip">Twin <b>Cinema</b></span>"';
  255. var span = document.getElementById('toStrip');
  256. goog.dom.Range.createFromNodeContents(span).select();
  257. FORMATTER.removeFormatting_();
  258. assertEquals(
  259. 'Text should be the same, with no non-breaking spaces', '"Twin Cinema"',
  260. div.innerHTML);
  261. FIELDMOCK.$verify();
  262. }
  263. /**
  264. * @bug 992795
  265. */
  266. function testRemoveFormattingNestedDivs() {
  267. var html =
  268. FORMATTER.removeFormattingWorker_('<div>1</div><div><div>2</div></div>');
  269. goog.testing.dom.assertHtmlMatches('1<br>2', html);
  270. }
  271. function testTheJavascriptReplaceMetacharacters() {
  272. var div = document.getElementById('html');
  273. div.innerHTML = '123 $< $> $" $& $$ $` $\' 456';
  274. var expected = '123 $&lt; $&gt; $" $&amp; $$ $` $\' 456' +
  275. (goog.userAgent.product.SAFARI ? '<br>' : '');
  276. // No idea why these <br> appear, but they're fairly insignificant anyways.
  277. goog.dom.Range.createFromNodeContents(div).select();
  278. FORMATTER.removeFormatting_();
  279. assertHTMLEquals(
  280. 'String.prototype.replace metacharacters should not trigger', expected,
  281. div.innerHTML);
  282. }
  283. /**
  284. * Test that when we perform remove formatting on an entire table,
  285. * that the visual look is similar to as if there was a table there.
  286. */
  287. function testRemoveFormattingForTableFormatting() {
  288. // We preserve the table formatting as much as possible.
  289. // Spaces separate TD's, <br>'s separate TR's.
  290. // <br>'s separate the start and end of a table.
  291. var html = '<table><tr><td>cell00</td><td>cell01</td></tr>' +
  292. '<tr><td>cell10</td><td>cell11</td></tr></table>';
  293. html = FORMATTER.removeFormattingWorker_(html);
  294. assertHTMLEquals('<br>cell00 cell01<br>cell10 cell11<br>', html);
  295. }
  296. /**
  297. * @bug 1319715
  298. */
  299. function testRemoveFormattingDoesNotShrinkSelection() {
  300. var div = document.getElementById('html');
  301. div.innerHTML = '<div>l </div><div><br><b>a</b>foo bar</div>';
  302. var div2 = div.lastChild;
  303. goog.dom.Range.createFromNodes(div2.firstChild, 0, div2.lastChild, 7)
  304. .select();
  305. FORMATTER.removeFormatting_();
  306. var range = goog.dom.Range.createFromWindow();
  307. assertEquals('Correct text should be selected', 'afoo bar', range.getText());
  308. // We have to trim out the leading BR in IE due to execCommand issues,
  309. // so it isn't sent off to the removeFormattingWorker.
  310. // Workaround for broken removeFormat in old webkit added an extra
  311. // <br> to the end of the html.
  312. var html = '<div>l </div><br class="GECKO WEBKIT">afoo bar' +
  313. (goog.editor.BrowserFeature.ADDS_NBSPS_IN_REMOVE_FORMAT ? '<br>' : '');
  314. if (goog.userAgent.EDGE) { // TODO(user): I have no idea where this comes from
  315. html = html.replace(' class="GECKO WEBKIT"', '');
  316. }
  317. goog.testing.dom.assertHtmlContentsMatch(html, div);
  318. FIELDMOCK.$verify();
  319. }
  320. /**
  321. * @bug 1447374
  322. */
  323. function testInsideListRemoveFormat() {
  324. var div = document.getElementById('html');
  325. div.innerHTML = '<ul><li>one</li><li><b>two</b></li><li>three</li></ul>';
  326. var twoLi = div.firstChild.childNodes[1];
  327. goog.dom.Range.createFromNodeContents(twoLi).select();
  328. expectedFailures.expectFailureFor(
  329. goog.userAgent.IE,
  330. 'IE adds the "two" to the "three" li, and leaves empty B tags.');
  331. expectedFailures.expectFailureFor(
  332. goog.userAgent.WEBKIT || goog.userAgent.EDGE,
  333. 'WebKit and Edge leave the "two" orphaned outside of an li but ' +
  334. 'inside the ul (invalid HTML).');
  335. expectedFailures.run(function() {
  336. FORMATTER.removeFormatting_();
  337. // Test that we split the list.
  338. assertHTMLEquals(
  339. '<ul><li>one</li></ul><br>two<ul><li>three</li></ul>', div.innerHTML);
  340. FIELDMOCK.$verify();
  341. });
  342. }
  343. function testFullListRemoveFormat() {
  344. var div = document.getElementById('html');
  345. div.innerHTML = '<ul><li>one</li><li><b>two</b></li><li>three</li></ul>after';
  346. goog.dom.Range.createFromNodeContents(div.firstChild).select();
  347. // Note: This may just be a createFromNodeContents issue, as
  348. // I can't ever make this happen with real user selection.
  349. expectedFailures.expectFailureFor(
  350. goog.userAgent.IE,
  351. 'IE combines everything into a single LI and leaves the UL.');
  352. expectedFailures.run(function() {
  353. FORMATTER.removeFormatting_();
  354. // Test that we completely remove the list.
  355. assertHTMLEquals('<br>one<br>two<br>threeafter', div.innerHTML);
  356. FIELDMOCK.$verify();
  357. });
  358. }
  359. /**
  360. * @bug 1440935
  361. */
  362. function testPartialListRemoveFormat() {
  363. var div = document.getElementById('html');
  364. div.innerHTML = '<ul><li>one</li><li>two</li><li>three</li></ul>after';
  365. // Select "two three after".
  366. goog.dom.Range
  367. .createFromNodes(div.firstChild.childNodes[1], 0, div.lastChild, 5)
  368. .select();
  369. expectedFailures.expectFailureFor(
  370. goog.userAgent.IE, 'IE leaves behind an empty LI.');
  371. expectedFailures.expectFailureFor(
  372. goog.userAgent.WEBKIT, 'WebKit completely loses the "one".');
  373. if (goog.userAgent.EDGE) {
  374. // Edge leaves "two" and "threeafter" orphaned outside of an li but inside
  375. // the ul (invalid HTML).
  376. // Skip this test instead of using expectedFailures because this failure
  377. // mode wrecks the DOM and causes later tests to fail as well.
  378. return;
  379. }
  380. expectedFailures.run(function() {
  381. FORMATTER.removeFormatting_();
  382. // Test that we leave the list start alone.
  383. assertHTMLEquals(
  384. '<ul><li>one</li></ul><br>two<br>threeafter', div.innerHTML);
  385. FIELDMOCK.$verify();
  386. });
  387. }
  388. function testBasicRemoveFormatting() {
  389. // IE will clobber the editable div.
  390. // Note: I can't repro this using normal user selections.
  391. if (goog.userAgent.IE) {
  392. return;
  393. }
  394. var div = document.getElementById('html');
  395. div.innerHTML = '<b>bold<i>italic</i></b>';
  396. goog.dom.Range.createFromNodeContents(div).select();
  397. expectedFailures.expectFailureFor(
  398. goog.editor.BrowserFeature.ADDS_NBSPS_IN_REMOVE_FORMAT,
  399. 'The workaround for the nbsp bug adds an extra br at the end.');
  400. expectedFailures.run(function() {
  401. FORMATTER.removeFormatting_();
  402. assertHTMLEquals('bolditalic' + insertImageBoldGarbage, div.innerHTML);
  403. FIELDMOCK.$verify();
  404. });
  405. }
  406. /**
  407. * @bug 1480260
  408. */
  409. function testPartialBasicRemoveFormatting() {
  410. var div = document.getElementById('html');
  411. div.innerHTML = '<b>bold<i>italic</i></b>';
  412. goog.dom.Range
  413. .createFromNodes(
  414. div.firstChild.firstChild, 2, div.firstChild.lastChild.firstChild, 3)
  415. .select();
  416. expectedFailures.expectFailureFor(
  417. WEBKIT_BEFORE_CHROME_8,
  418. 'WebKit just gets this all wrong. Everything stays bold and ' +
  419. '"lditalic" gets italicised.');
  420. expectedFailures.run(function() {
  421. FORMATTER.removeFormatting_();
  422. assertHTMLEquals('<b>bo</b>ldita<b><i>lic</i></b>', div.innerHTML);
  423. FIELDMOCK.$verify();
  424. });
  425. }
  426. /**
  427. * @bug 3075557
  428. */
  429. function testRemoveFormattingLinkedImageBorderZero() {
  430. var testHtml = '<a href="http://www.google.com/">' +
  431. '<img src="http://www.google.com/images/logo.gif" border="0"></a>';
  432. var div = document.getElementById('html');
  433. div.innerHTML = testHtml + controlHtml;
  434. goog.dom.Range.createFromNodeContents(div).select();
  435. FORMATTER.removeFormatting_();
  436. expectedFailures.expectFailureFor(
  437. goog.userAgent.WEBKIT, 'WebKit removes the image entirely, see ' +
  438. 'https://bugs.webkit.org/show_bug.cgi?id=13125 .');
  439. expectedFailures.run(function() {
  440. assertHTMLEquals(
  441. 'Image\'s border=0 should not be removed during remove formatting',
  442. testHtml + controlCleanHtml, div.innerHTML);
  443. FIELDMOCK.$verify();
  444. });
  445. }
  446. /**
  447. * @bug 3075557
  448. */
  449. function testRemoveFormattingLinkedImageBorderNonzero() {
  450. var testHtml = '<a href="http://www.google.com/">' +
  451. '<img src="http://www.google.com/images/logo.gif" border="1"></a>';
  452. var div = document.getElementById('html');
  453. div.innerHTML = testHtml + controlHtml;
  454. goog.dom.Range.createFromNodeContents(div).select();
  455. FORMATTER.removeFormatting_();
  456. expectedFailures.expectFailureFor(
  457. goog.userAgent.WEBKIT, 'WebKit removes the image entirely, see ' +
  458. 'https://bugs.webkit.org/show_bug.cgi?id=13125 .');
  459. expectedFailures.run(function() {
  460. assertHTMLEquals(
  461. 'Image\'s border should be removed during remove formatting' +
  462. ' if non-zero',
  463. testHtml.replace(' border="1"', '') + controlCleanHtml, div.innerHTML);
  464. FIELDMOCK.$verify();
  465. });
  466. }
  467. /**
  468. * @bug 3075557
  469. */
  470. function testRemoveFormattingUnlinkedImage() {
  471. var testHtml = '<img src="http://www.google.com/images/logo.gif" border="0">';
  472. var div = document.getElementById('html');
  473. div.innerHTML = testHtml + controlHtml;
  474. goog.dom.Range.createFromNodeContents(div).select();
  475. FORMATTER.removeFormatting_();
  476. expectedFailures.expectFailureFor(
  477. goog.userAgent.WEBKIT, 'WebKit removes the image entirely, see ' +
  478. 'https://bugs.webkit.org/show_bug.cgi?id=13125 .');
  479. expectedFailures.run(function() {
  480. assertHTMLEquals(
  481. 'Image\'s border=0 should not be removed during remove formatting' +
  482. ' even if not wrapped by a link',
  483. testHtml + controlCleanHtml, div.innerHTML);
  484. FIELDMOCK.$verify();
  485. });
  486. }
  487. /**
  488. * @bug 3075557
  489. */
  490. function testRemoveFormattingLinkedImageDeep() {
  491. var testHtml = '<a href="http://www.google.com/"><b>hello' +
  492. '<img src="http://www.google.com/images/logo.gif" border="0">' +
  493. 'world</b></a>';
  494. var div = document.getElementById('html');
  495. div.innerHTML = testHtml + controlHtml;
  496. goog.dom.Range.createFromNodeContents(div).select();
  497. FORMATTER.removeFormatting_();
  498. expectedFailures.expectFailureFor(
  499. WEBKIT_BEFORE_CHROME_8, 'WebKit removes the image entirely, see ' +
  500. 'https://bugs.webkit.org/show_bug.cgi?id=13125 .');
  501. expectedFailures.run(function() {
  502. assertHTMLEquals(
  503. 'Image\'s border=0 should not be removed during remove formatting' +
  504. ' even if deep inside anchor tag',
  505. testHtml.replace(/<\/?b>/g, '') + controlCleanHtml +
  506. insertImageBoldGarbage,
  507. div.innerHTML);
  508. FIELDMOCK.$verify();
  509. });
  510. }
  511. function testFullTableRemoveFormatting() {
  512. // Something goes horrible wrong in case 1 below. It was crashing all
  513. // WebKit browsers, and now seems to be giving errors as it is trying
  514. // to perform remove formatting on the little expected failures window
  515. // instead of the dom we select. WTF. Since I'm gutting this code,
  516. // I'm not going to look into this anymore right now. For what its worth,
  517. // I can't repro any issues in standalone TrogEdit.
  518. if (goog.userAgent.WEBKIT) {
  519. return;
  520. }
  521. var div = document.getElementById('html');
  522. // WebKit has an extra BR in case 2.
  523. expectedFailures.expectFailureFor(
  524. goog.userAgent.IE,
  525. 'IE clobbers the editable node in case 2 (can\'t repro with real ' +
  526. 'user selections). IE doesn\'t remove the table in case 1.');
  527. expectedFailures.run(function() {
  528. // When a full table is selected, we remove it completely.
  529. div.innerHTML = 'foo<table><tr><td>bar</td></tr></table>baz1';
  530. goog.dom.Range.createFromNodeContents(div.childNodes[1]).select();
  531. FORMATTER.removeFormatting_();
  532. assertHTMLEquals('foo<br>bar<br>baz1', div.innerHTML);
  533. FIELDMOCK.$verify();
  534. // Remove the full table when it is selected with additional
  535. // contents too.
  536. div.innerHTML = 'foo<table><tr><td>bar</td></tr></table>baz2';
  537. goog.dom.Range.createFromNodes(div.firstChild, 0, div.lastChild, 1)
  538. .select();
  539. FORMATTER.removeFormatting_();
  540. assertHTMLEquals('foo<br>bar<br>baz2', div.innerHTML);
  541. FIELDMOCK.$verify();
  542. // We should still remove the table, even if the selection is inside the
  543. // table and it is fully selected.
  544. div.innerHTML = 'foo<table><tr><td id=\'td\'>bar</td></tr></table>baz3';
  545. goog.dom.Range.createFromNodeContents(goog.dom.getElement('td').firstChild)
  546. .select();
  547. FORMATTER.removeFormatting_();
  548. assertHTMLEquals('foo<br>bar<br>baz3', div.innerHTML);
  549. FIELDMOCK.$verify();
  550. });
  551. }
  552. function testInsideTableRemoveFormatting() {
  553. var div = document.getElementById('html');
  554. div.innerHTML =
  555. '<table><tr><td><b id="b">foo</b></td></tr><tr><td>ba</td></tr></table>';
  556. goog.dom.Range.createFromNodeContents(goog.dom.getElement('b')).select();
  557. // Webkit adds some apple style span crap during execCommand("removeFormat")
  558. // Our workaround for the nbsp bug removes these, but causes worse problems.
  559. // See bugs.webkit.org/show_bug.cgi?id=29164 for more details.
  560. expectedFailures.expectFailureFor(
  561. WEBKIT_BEFORE_CHROME_8 &&
  562. !goog.editor.BrowserFeature.ADDS_NBSPS_IN_REMOVE_FORMAT,
  563. 'Extra apple-style-spans');
  564. expectedFailures.run(function() {
  565. FORMATTER.removeFormatting_();
  566. // Only remove styling from inside tables.
  567. assertHTMLEquals(
  568. '<table><tr><td>foo' + insertImageBoldGarbage +
  569. '</td></tr><tr><td>ba</td></tr></table>',
  570. div.innerHTML);
  571. FIELDMOCK.$verify();
  572. });
  573. }
  574. function testPartialTableRemoveFormatting() {
  575. if (goog.userAgent.IE) {
  576. // IE returns an "unspecified error" which seems to be beyond
  577. // ExpectedFailures' ability to catch.
  578. return;
  579. }
  580. var div = document.getElementById('html');
  581. div.innerHTML = 'bar<table><tr><td><b id="b">foo</b></td></tr>' +
  582. '<tr><td><i>banana</i></td></tr></table><div id="baz">' +
  583. 'baz</div>';
  584. // Select from the "oo" inside the b tag to the end of "baz".
  585. goog.dom.Range
  586. .createFromNodes(
  587. goog.dom.getElement('b').firstChild, 1,
  588. goog.dom.getElement('baz').firstChild, 3)
  589. .select();
  590. // All browsers currently clobber the table cells that are selected.
  591. expectedFailures.expectFailureFor(goog.userAgent.WEBKIT);
  592. expectedFailures.run(function() {
  593. FORMATTER.removeFormatting_();
  594. // Only remove styling from inside tables.
  595. assertHTMLEquals(
  596. 'bar<table><tr><td><b id="b">f</b>oo</td></tr>' +
  597. '<tr><td>banana</td></tr></table>baz',
  598. div.innerHTML);
  599. FIELDMOCK.$verify();
  600. });
  601. }
  602. // Runs tests knowing some browsers will fail, because the new
  603. // table functionality hasn't been implemented in them yet.
  604. function runExpectingFailuresForUnimplementedBrowsers(func) {
  605. if (goog.userAgent.IE) {
  606. // IE returns an "unspecified error" which seems to be beyond
  607. // ExpectedFailures' ability to catch.
  608. return;
  609. }
  610. expectedFailures.expectFailureFor(
  611. goog.userAgent.IE, 'Proper behavior not yet implemented for IE.');
  612. expectedFailures.expectFailureFor(
  613. goog.userAgent.WEBKIT, 'Proper behavior not yet implemented for WebKit.');
  614. expectedFailures.run(func);
  615. }
  616. function testTwoTablesSelectedFullyRemoveFormatting() {
  617. runExpectingFailuresForUnimplementedBrowsers(function() {
  618. var div = document.getElementById('html');
  619. // When two tables are fully selected, we remove them completely.
  620. div.innerHTML = '<table><tr><td>foo</td></tr></table>' +
  621. '<table><tr><td>bar</td></tr></table>';
  622. goog.dom.Range.createFromNodes(div.firstChild, 0, div.lastChild, 1)
  623. .select();
  624. FORMATTER.removeFormatting_();
  625. assertHTMLEquals('<br>foo<br><br>bar<br>', div.innerHTML);
  626. FIELDMOCK.$verify();
  627. });
  628. }
  629. function testTwoTablesSelectedFullyInsideRemoveFormatting() {
  630. if (goog.userAgent.WEBKIT) {
  631. // Something goes very wrong here, but it did before
  632. // Julie started writing v2. Will address when converting
  633. // safari to v2.
  634. return;
  635. }
  636. runExpectingFailuresForUnimplementedBrowsers(function() {
  637. var div = document.getElementById('html');
  638. // When two tables are selected from inside but fully,
  639. // also remove them completely.
  640. div.innerHTML = '<table><tr><td id="td1">foo</td></tr></table>' +
  641. '<table><tr><td id="td2">bar</td></tr></table>';
  642. goog.dom.Range
  643. .createFromNodes(
  644. goog.dom.getElement('td1').firstChild, 0,
  645. goog.dom.getElement('td2').firstChild, 3)
  646. .select();
  647. FORMATTER.removeFormatting_();
  648. assertHTMLEquals('<br>foo<br><br>bar<br>', div.innerHTML);
  649. FIELDMOCK.$verify();
  650. });
  651. }
  652. function testTwoTablesSelectedFullyAndPartiallyRemoveFormatting() {
  653. runExpectingFailuresForUnimplementedBrowsers(function() {
  654. var div = document.getElementById('html');
  655. // Two tables selected, one fully, one partially. Remove
  656. // only the fully selected one and remove styles only from
  657. // partially selected one.
  658. div.innerHTML = '<table><tr><td id="td1">foo</td></tr></table>' +
  659. '<table><tr><td id="td2"><b>bar<b></td></tr></table>';
  660. goog.dom.Range
  661. .createFromNodes(
  662. goog.dom.getElement('td1').firstChild, 0,
  663. goog.dom.getElement('td2').firstChild.firstChild, 2)
  664. .select();
  665. FORMATTER.removeFormatting_();
  666. var expectedHtml = '<br>foo<br>' +
  667. '<table><tr><td id="td2">ba<b>r</b></td></tr></table>';
  668. if (goog.userAgent.EDGE) {
  669. // TODO(user): Edge inserts an extra empty <b> tag but is otherwise correct
  670. expectedHtml = expectedHtml.replace('</b>', '<b></b></b>');
  671. }
  672. assertHTMLEquals(expectedHtml, div.innerHTML);
  673. FIELDMOCK.$verify();
  674. });
  675. }
  676. function testTwoTablesSelectedPartiallyRemoveFormatting() {
  677. runExpectingFailuresForUnimplementedBrowsers(function() {
  678. var div = document.getElementById('html');
  679. // Two tables selected, both partially. Don't remove tables,
  680. // but remove styles.
  681. div.innerHTML = '<table><tr><td id="td1">f<i>o</i>o</td></tr></table>' +
  682. '<table><tr><td id="td2">b<b>a</b>r</td></tr></table>';
  683. goog.dom.Range
  684. .createFromNodes(
  685. goog.dom.getElement('td1').firstChild, 1,
  686. goog.dom.getElement('td2').childNodes[1], 1)
  687. .select();
  688. FORMATTER.removeFormatting_();
  689. assertHTMLEquals(
  690. '<table><tr><td id="td1">foo</td></tr></table>' +
  691. '<table><tr><td id="td2">bar</td></tr></table>',
  692. div.innerHTML);
  693. FIELDMOCK.$verify();
  694. });
  695. }
  696. /**
  697. * Test a random snippet from Google News (Google News has complicated
  698. * dom structure, including tables, links, images, etc).
  699. */
  700. function testRandomGoogleNewsSnippetRemoveFormatting() {
  701. if (goog.userAgent.IE) {
  702. // IE returns an "unspecified error" which seems to be beyond
  703. // ExpectedFailures' ability to catch.
  704. return;
  705. }
  706. var div = document.getElementById('html');
  707. div.innerHTML =
  708. '<font size="-3"><br></font><table align="right" border="0" ' +
  709. 'cellpadding="0" cellspacing="0"><tbody><tr><td style="padding-left:' +
  710. '6px;" valign="top" width="80" align="center"><a href="http://www.wash' +
  711. 'ingtonpost.com/wp-dyn/content/article/2008/11/11/AR2008111101090.htm' +
  712. 'l" + id="s-skHRvWH7ryqkcA4caGv0QQ:u-AFQjCNG3vx1HJOxKxMQPzCvYOVRE0JUDe' +
  713. 'Q:r-1-0i_1268233361_6_H0_MH20_PL60"><img src="http://news.google.com/' +
  714. 'news?imgefp=4LFiNNP62TgJ&amp;imgurl=media3.washingtonpost.com/wp-dyn/' +
  715. 'content/photo/2008/11/11/PH2008111101091.jpg" alt="" width="60" ' +
  716. 'border="1" height="80"><br><font size="-2">Washington Post</font></a>' +
  717. '</td></tr></tbody></table><a href="http://www.nme.com/news/britney-' +
  718. 'spears/40995" id="s-xZUO-t0c1IpsVjyJj0rgxw:u-AFQjCNEZAMQCseEW6uTgXI' +
  719. 'iPvAMHe_0B4A:r-1-0_1268233361_6_H0_MH20_PL60"><b>Britney\'s son ' +
  720. 'released from hospital</b></a><br><font size="-1"><b><font color=' +
  721. '"#6f6f6f">NME.com&nbsp;-</font> <nobr>53 minutes ago</nobr></b>' +
  722. '</font><br><font size="-1">Britney Spears� youngest son Jayden James ' +
  723. 'has been released from hospital, having been admitted on Sunday after' +
  724. ' suffering a severe reaction to something he ingested.</font><br><fon' +
  725. 'tsize="-1"><a href="http://www.celebrity-gossip.net/celebrities/holly' +
  726. 'wood/britney-and-jamie-lynn-spears-alligator-alley-208944/" id="s-nM' +
  727. 'PzHclcMG0J2WZkw9gnVQ:u-AFQjCNHal08usOQ5e5CAQsck2yGsTYeGVQ">Britney ' +
  728. 'and Jamie Lynn Spears: Alligator Alley!</a> <font size="-1" color=' +
  729. '"#6f6f6f"><nobr>The Gossip Girls</nobr></font></font><br><font size=' +
  730. '"-1"><a href="http://foodconsumer.org/7777/8888/Other_N_ews_51/111101' +
  731. '362008_Allergy_incident_could_spell_custody_trouble_for_Britney_Spear' +
  732. 's.shtml" id="s-2lMNDY4joOprVvkkY_b-6A:u-AFQjCNGAeFNutMEbSg5zAvrh5reBF' +
  733. 'lqUmA">Allergy incident could spell trouble for Britney Spears</a> ' +
  734. '<font size="-1" color="#6f6f6f"><nobr>Food Consumer</nobr></font>' +
  735. '</font><br><font class="p" size="-1"><a href="http://www.people.com/' +
  736. 'people/article/0,,20239458,00.html" id="s-x9thwVUYVET0ZJOnkkcsjw:u-A' +
  737. 'FQjCNE99eijVIrezr9AFRjLkmo5j_Jr7A"><nobr>People Magazine</nobr></a>&nb' +
  738. 'sp;- <a href="http://www.eonline.com/uberblog/b68226_hospital_run_cou' +
  739. 'ld_cost_britney_custody.html" id="s-kYt5LHDhlDnhUL9kRLuuwA:u-AFQjCNF8' +
  740. '8eOy2utriYuF0icNrZQPzwK8gg"><nobr>E! Online</nobr></a>&nbsp;- <a href' +
  741. '="http://justjared.buzznet.com/2008/11/11/britney-spears-alligator-fa' +
  742. 'rm/" id="s--VDy1fyacNvaRo_aXb02Dw:u-AFQjCNEn0Rz3wg0PMwDdzKTDug-9k5W6y' +
  743. 'g"><nobr>Just Jared</nobr></a>&nbsp;- <a href="http://www.efluxmedia.' +
  744. 'com/news_Britney_Spears_Son_Released_from_Hospital_28696.html" id="s-' +
  745. '8oX6hVDe4Qbcl1x5Rua_EA:u-AFQjCNEpn3nOHA8EB0pxJAPf6diOicMRDg"><nobr>eF' +
  746. 'luxMedia</nobr></a></font><br><font class="p" size="-1"><a class="p" ' +
  747. 'href="http://news.google.com/news?ncl=1268233361&amp;hl=en"><nobr><b>' +
  748. 'all 950 news articles&nbsp;�</b></nobr></a></font>';
  749. // Select it all.
  750. goog.dom.Range.createFromNodeContents(div).select();
  751. expectedFailures.expectFailureFor(
  752. WEBKIT_BEFORE_CHROME_8,
  753. 'WebKit barfs apple-style-spans all over the place, and removes links.');
  754. expectedFailures.run(function() {
  755. FORMATTER.removeFormatting_();
  756. // Leave links and images alone, remove all other formatting.
  757. assertHTMLEquals(
  758. '<br><br><a href="http://www.washingtonpost.com/wp-dyn/' +
  759. 'content/article/2008/11/11/AR2008111101090.html"><img src="http://n' +
  760. 'ews.google.com/news?imgefp=4LFiNNP62TgJ&amp;imgurl=media3.washingto' +
  761. 'npost.com/wp-dyn/content/photo/2008/11/11/PH2008111101091.jpg"><br>' +
  762. 'Washington Post</a><br><a href="http://www.nme.com/news/britney-spe' +
  763. 'ars/40995">Britney\'s son released from hospital</a><br>NME.com - 5' +
  764. '3 minutes ago<br>Britney Spears� youngest son Jayden James has been' +
  765. ' released from hospital, having been admitted on Sunday after suffe' +
  766. 'ring a severe reaction to something he ingested.<br><a href="http:/' +
  767. '/www.celebrity-gossip.net/celebrities/hollywood/britney-and-jamie-l' +
  768. 'ynn-spears-alligator-alley-208944/">Britney and Jamie Lynn Spears: ' +
  769. 'Alligator Alley!</a> The Gossip Girls<br><a href="http://foodconsum' +
  770. 'er.org/7777/8888/Other_N_ews_51/111101362008_Allergy_incident_could' +
  771. '_spell_custody_trouble_for_Britney_Spears.shtml">Allergy incident c' +
  772. 'ould spell trouble for Britney Spears</a> Food Consumer<br><a href=' +
  773. '"http://www.people.com/people/article/0,,20239458,00.html">People M' +
  774. 'agazine</a> - <a href="http://www.eonline.com/uberblog/b68226_hospi' +
  775. 'tal_run_could_cost_britney_custody.html">E! Online</a> - <a href="h' +
  776. 'ttp://justjared.buzznet.com/2008/11/11/britney-spears-alligator-far' +
  777. 'm/">Just Jared</a> - <a href="http://www.efluxmedia.com/news_Britne' +
  778. 'y_Spears_Son_Released_from_Hospital_28696.html">eFluxMedia</a><br><' +
  779. 'a href="http://news.google.com/news?ncl=1268233361&amp;hl=en">all 9' +
  780. '50 news articles �</a>' + insertImageFontGarbage,
  781. div.innerHTML);
  782. FIELDMOCK.$verify();
  783. });
  784. }
  785. function testRangeDelimitedByRanges() {
  786. var abcde = goog.dom.getElement('abcde').firstChild;
  787. var start = goog.dom.Range.createFromNodes(abcde, 1, abcde, 2);
  788. var end = goog.dom.Range.createFromNodes(abcde, 3, abcde, 4);
  789. goog.testing.dom.assertRangeEquals(
  790. abcde, 1, abcde, 4,
  791. goog.editor.plugins.RemoveFormatting.createRangeDelimitedByRanges_(
  792. start, end));
  793. }
  794. function testGetTableAncestor() {
  795. var div = document.getElementById('html');
  796. div.innerHTML = 'foo<table><tr><td>foo</td></tr></table>bar';
  797. assertTrue(
  798. 'Full table is in table',
  799. !!FORMATTER.getTableAncestor_(div.childNodes[1]));
  800. assertFalse(
  801. 'Outside of table', !!FORMATTER.getTableAncestor_(div.firstChild));
  802. assertTrue(
  803. 'Table cell is in table',
  804. !!FORMATTER.getTableAncestor_(
  805. div.childNodes[1].firstChild.firstChild.firstChild));
  806. goog.dom.setTextContent(div, 'foo');
  807. assertNull(
  808. 'No table inside field.', FORMATTER.getTableAncestor_(div.childNodes[0]));
  809. }
  810. /**
  811. * @bug 1272905
  812. */
  813. function testHardReturnsInHeadersPreserved() {
  814. var div = document.getElementById('html');
  815. div.innerHTML = '<h1>abcd</h1><h2>efgh</h2><h3>ijkl</h3>';
  816. // Select efgh.
  817. goog.dom.Range.createFromNodeContents(div.childNodes[1]).select();
  818. FORMATTER.removeFormatting_();
  819. expectedFailures.expectFailureFor(
  820. goog.userAgent.IE, 'Proper behavior not yet implemented for IE.');
  821. expectedFailures.expectFailureFor(
  822. goog.userAgent.WEBKIT, 'Proper behavior not yet implemented for WebKit.');
  823. expectedFailures.run(function() {
  824. assertHTMLEquals('<h1>abcd</h1><br>efgh<h3>ijkl</h3>', div.innerHTML);
  825. });
  826. // Select ijkl.
  827. goog.dom.Range.createFromNodeContents(div.lastChild).select();
  828. FORMATTER.removeFormatting_();
  829. expectedFailures.expectFailureFor(
  830. goog.userAgent.IE, 'Proper behavior not yet implemented for IE.');
  831. expectedFailures.expectFailureFor(
  832. goog.userAgent.WEBKIT, 'Proper behavior not yet implemented for WebKit.');
  833. expectedFailures.run(function() {
  834. assertHTMLEquals('<h1>abcd</h1><br>efgh<br>ijkl', div.innerHTML);
  835. });
  836. // Select abcd.
  837. goog.dom.Range.createFromNodeContents(div.firstChild).select();
  838. FORMATTER.removeFormatting_();
  839. expectedFailures.expectFailureFor(
  840. goog.userAgent.IE, 'Proper behavior not yet implemented for IE.');
  841. expectedFailures.expectFailureFor(
  842. goog.userAgent.WEBKIT, 'Proper behavior not yet implemented for WebKit.');
  843. expectedFailures.run(function() {
  844. assertHTMLEquals('<br>abcd<br>efgh<br>ijkl', div.innerHTML);
  845. });
  846. }
  847. function testKeyboardShortcut_space() {
  848. FIELDMOCK.$reset();
  849. FIELDMOCK.execCommand(
  850. goog.editor.plugins.RemoveFormatting.REMOVE_FORMATTING_COMMAND);
  851. FIELDMOCK.$replay();
  852. var e = {};
  853. var key = ' ';
  854. var result = FORMATTER.handleKeyboardShortcut(e, key, true);
  855. assertTrue(result);
  856. FIELDMOCK.$verify();
  857. }
  858. function testKeyboardShortcut_other() {
  859. FIELDMOCK.$reset();
  860. FIELDMOCK.$replay();
  861. var e = {};
  862. var key = 'x';
  863. var result = FORMATTER.handleKeyboardShortcut(e, key, true);
  864. assertFalse(result);
  865. FIELDMOCK.$verify();
  866. }
  867. function testCustomKeyboardShortcut_custom() {
  868. FIELDMOCK.$reset();
  869. FIELDMOCK.execCommand(
  870. goog.editor.plugins.RemoveFormatting.REMOVE_FORMATTING_COMMAND);
  871. FIELDMOCK.$replay();
  872. var e = {};
  873. var key = '\\';
  874. FORMATTER.setKeyboardShortcutKey(key);
  875. var result = FORMATTER.handleKeyboardShortcut(e, key, true);
  876. assertTrue(result);
  877. FIELDMOCK.$verify();
  878. }
  879. function testCustomKeyboardShortcut_default() {
  880. FIELDMOCK.$reset();
  881. FIELDMOCK.$replay();
  882. var e = {};
  883. var key = ' ';
  884. FORMATTER.setKeyboardShortcutKey('\\');
  885. var result = FORMATTER.handleKeyboardShortcut(e, key, true);
  886. assertFalse(result);
  887. FIELDMOCK.$verify();
  888. }
  889. function testKeyboardShortcut_withBothModifierKeys() {
  890. FIELDMOCK.$reset();
  891. FIELDMOCK.$replay();
  892. var e = {};
  893. e.metaKey = true;
  894. e.ctrlKey = true;
  895. var key = ' ';
  896. var result = FORMATTER.handleKeyboardShortcut(e, key, true);
  897. assertFalse(result);
  898. FIELDMOCK.$verify();
  899. }
  900. function testKeyboardShortcut_withMetaKeyAndShiftKey() {
  901. FIELDMOCK.$reset();
  902. FIELDMOCK.$replay();
  903. var e = {};
  904. e.metaKey = true;
  905. e.shiftKey = true;
  906. var key = ' ';
  907. var result = FORMATTER.handleKeyboardShortcut(e, key, true);
  908. assertFalse(result);
  909. FIELDMOCK.$verify();
  910. }
  911. function testKeyboardShortcut_withCtrlKeyAndShiftKey() {
  912. FIELDMOCK.$reset();
  913. FIELDMOCK.$replay();
  914. var e = {};
  915. e.ctrlKey = true;
  916. e.shiftKey = true;
  917. var key = ' ';
  918. var result = FORMATTER.handleKeyboardShortcut(e, key, true);
  919. assertFalse(result);
  920. FIELDMOCK.$verify();
  921. }