basictextformatter_test.js 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300
  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.BasicTextFormatterTest');
  15. goog.setTestOnly('goog.editor.plugins.BasicTextFormatterTest');
  16. goog.require('goog.array');
  17. goog.require('goog.dom');
  18. goog.require('goog.dom.Range');
  19. goog.require('goog.dom.TagName');
  20. goog.require('goog.editor.BrowserFeature');
  21. goog.require('goog.editor.Command');
  22. goog.require('goog.editor.Field');
  23. goog.require('goog.editor.Plugin');
  24. goog.require('goog.editor.plugins.BasicTextFormatter');
  25. goog.require('goog.object');
  26. goog.require('goog.style');
  27. goog.require('goog.testing.ExpectedFailures');
  28. goog.require('goog.testing.LooseMock');
  29. goog.require('goog.testing.PropertyReplacer');
  30. goog.require('goog.testing.editor.FieldMock');
  31. goog.require('goog.testing.editor.TestHelper');
  32. goog.require('goog.testing.jsunit');
  33. goog.require('goog.testing.mockmatchers');
  34. goog.require('goog.userAgent');
  35. goog.require('goog.userAgent.product');
  36. var stubs;
  37. var SAVED_HTML;
  38. var FIELDMOCK;
  39. var FORMATTER;
  40. var ROOT;
  41. var HELPER;
  42. var OPEN_SUB;
  43. var CLOSE_SUB;
  44. var OPEN_SUPER;
  45. var CLOSE_SUPER;
  46. var MOCK_BLOCKQUOTE_STYLE = 'border-left: 1px solid gray;';
  47. var MOCK_GET_BLOCKQUOTE_STYLES = function() {
  48. return MOCK_BLOCKQUOTE_STYLE;
  49. };
  50. var REAL_FIELD;
  51. var REAL_PLUGIN;
  52. var expectedFailures;
  53. function setUpPage() {
  54. stubs = new goog.testing.PropertyReplacer();
  55. SAVED_HTML = goog.dom.getElement('html').innerHTML;
  56. FIELDMOCK;
  57. FORMATTER;
  58. ROOT = goog.dom.getElement('root');
  59. HELPER;
  60. OPEN_SUB = goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('530') ?
  61. '<span class="Apple-style-span" style="vertical-align: sub;">' :
  62. '<sub>';
  63. CLOSE_SUB =
  64. goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('530') ?
  65. '</span>' :
  66. '</sub>';
  67. OPEN_SUPER =
  68. goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('530') ?
  69. '<span class="Apple-style-span" style="vertical-align: super;">' :
  70. '<sup>';
  71. CLOSE_SUPER =
  72. goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('530') ?
  73. '</span>' :
  74. '</sup>';
  75. expectedFailures = new goog.testing.ExpectedFailures();
  76. }
  77. function setUp() {
  78. FIELDMOCK = new goog.testing.editor.FieldMock();
  79. FORMATTER = new goog.editor.plugins.BasicTextFormatter();
  80. FORMATTER.fieldObject = FIELDMOCK;
  81. }
  82. function setUpRealField() {
  83. REAL_FIELD = new goog.editor.Field('real-field');
  84. REAL_PLUGIN = new goog.editor.plugins.BasicTextFormatter();
  85. REAL_FIELD.registerPlugin(REAL_PLUGIN);
  86. REAL_FIELD.makeEditable();
  87. }
  88. function setUpRealFieldIframe() {
  89. REAL_FIELD = new goog.editor.Field('iframe');
  90. FORMATTER = new goog.editor.plugins.BasicTextFormatter();
  91. REAL_FIELD.registerPlugin(FORMATTER);
  92. REAL_FIELD.makeEditable();
  93. }
  94. function selectRealField() {
  95. goog.dom.Range.createFromNodeContents(REAL_FIELD.getElement()).select();
  96. REAL_FIELD.dispatchSelectionChangeEvent();
  97. }
  98. function tearDown() {
  99. tearDownFontSizeTests();
  100. if (REAL_FIELD) {
  101. REAL_FIELD.makeUneditable();
  102. REAL_FIELD.dispose();
  103. REAL_FIELD = null;
  104. }
  105. expectedFailures.handleTearDown();
  106. stubs.reset();
  107. goog.dom.getElement('html').innerHTML = SAVED_HTML;
  108. }
  109. function setUpListAndBlockquoteTests() {
  110. var htmlDiv = document.getElementById('html');
  111. HELPER = new goog.testing.editor.TestHelper(htmlDiv);
  112. HELPER.setUpEditableElement();
  113. FIELDMOCK.getElement();
  114. FIELDMOCK.$anyTimes();
  115. FIELDMOCK.$returns(htmlDiv);
  116. }
  117. function tearDownHelper() {
  118. HELPER.tearDownEditableElement();
  119. HELPER.dispose();
  120. HELPER = null;
  121. }
  122. function tearDownListAndBlockquoteTests() {
  123. tearDownHelper();
  124. }
  125. function testIEList() {
  126. if (goog.userAgent.IE) {
  127. setUpListAndBlockquoteTests();
  128. FIELDMOCK.queryCommandValue('rtl').$returns(null);
  129. FIELDMOCK.$replay();
  130. var ul = goog.dom.getElement('outerUL');
  131. goog.dom.Range
  132. .createFromNodeContents(goog.dom.getFirstElementChild(ul).firstChild)
  133. .select();
  134. FORMATTER.fixIELists_();
  135. assertFalse('Unordered list must not have ordered type', ul.type == '1');
  136. var ol = goog.dom.getElement('ol');
  137. ol.type = 'disc';
  138. goog.dom.Range
  139. .createFromNodeContents(goog.dom.getFirstElementChild(ul).firstChild)
  140. .select();
  141. FORMATTER.fixIELists_();
  142. assertFalse('Ordered list must not have unordered type', ol.type == 'disc');
  143. ol.type = '1';
  144. goog.dom.Range
  145. .createFromNodeContents(goog.dom.getFirstElementChild(ul).firstChild)
  146. .select();
  147. FORMATTER.fixIELists_();
  148. assertTrue('Ordered list must retain ordered list type', ol.type == '1');
  149. tearDownListAndBlockquoteTests();
  150. }
  151. }
  152. function testWebKitList() {
  153. if (goog.userAgent.WEBKIT) {
  154. setUpListAndBlockquoteTests();
  155. FIELDMOCK.queryCommandValue('rtl').$returns(null);
  156. FIELDMOCK.$replay();
  157. var ul = document.getElementById('outerUL');
  158. var html = ul.innerHTML;
  159. goog.dom.Range.createFromNodeContents(ul).select();
  160. FORMATTER.fixSafariLists_();
  161. assertEquals('Contents of UL shouldn\'t change', html, ul.innerHTML);
  162. ul = document.getElementById('outerUL2');
  163. goog.dom.Range.createFromNodeContents(ul).select();
  164. FORMATTER.fixSafariLists_();
  165. var childULs = goog.dom.getElementsByTagName(goog.dom.TagName.UL, ul);
  166. assertEquals('UL should have one child UL', 1, childULs.length);
  167. tearDownListAndBlockquoteTests();
  168. }
  169. }
  170. function testGeckoListFont() {
  171. if (goog.userAgent.GECKO) {
  172. setUpListAndBlockquoteTests();
  173. FIELDMOCK.queryCommandValue(goog.editor.Command.DEFAULT_TAG)
  174. .$returns('BR')
  175. .$times(2);
  176. FIELDMOCK.$replay();
  177. var p = goog.dom.getElement('geckolist');
  178. var font = p.firstChild;
  179. goog.dom.Range.createFromNodeContents(font).select();
  180. retVal = FORMATTER.beforeInsertListGecko_();
  181. assertFalse('Workaround shouldn\'t be applied when not needed', retVal);
  182. goog.dom.removeChildren(font);
  183. goog.dom.Range.createFromNodeContents(font).select();
  184. var retVal = FORMATTER.beforeInsertListGecko_();
  185. assertTrue('Workaround should be applied when needed', retVal);
  186. document.execCommand('insertorderedlist', false, true);
  187. assertTrue(
  188. 'Font should be Courier',
  189. /courier/i.test(document.queryCommandValue('fontname')));
  190. tearDownListAndBlockquoteTests();
  191. }
  192. }
  193. function testSwitchListType() {
  194. if (!goog.userAgent.WEBKIT) {
  195. return;
  196. }
  197. // Test that we're not seeing https://bugs.webkit.org/show_bug.cgi?id=19539,
  198. // the type of multi-item lists.
  199. setUpListAndBlockquoteTests();
  200. FIELDMOCK.$replay();
  201. var list = goog.dom.getElement('switchListType');
  202. var parent = goog.dom.getParentElement(list);
  203. goog.dom.Range.createFromNodeContents(list).select();
  204. FORMATTER.execCommandInternal('insertunorderedlist');
  205. list = goog.dom.getFirstElementChild(parent);
  206. assertEquals(String(goog.dom.TagName.UL), list.tagName);
  207. assertEquals(
  208. 3, goog.dom.getElementsByTagNameAndClass(goog.dom.TagName.LI, null, list)
  209. .length);
  210. goog.dom.Range.createFromNodeContents(list).select();
  211. FORMATTER.execCommandInternal('insertorderedlist');
  212. list = goog.dom.getFirstElementChild(parent);
  213. assertEquals(String(goog.dom.TagName.OL), list.tagName);
  214. assertEquals(
  215. 3, goog.dom.getElementsByTagNameAndClass(goog.dom.TagName.LI, null, list)
  216. .length);
  217. tearDownListAndBlockquoteTests();
  218. }
  219. function testIsSilentCommand() {
  220. var commands =
  221. goog.object.getValues(goog.editor.plugins.BasicTextFormatter.COMMAND);
  222. var silentCommands =
  223. [goog.editor.plugins.BasicTextFormatter.COMMAND.CREATE_LINK];
  224. for (var i = 0; i < commands.length; i += 1) {
  225. var command = commands[i];
  226. var shouldBeSilent = goog.array.contains(silentCommands, command);
  227. var isSilent =
  228. goog.editor.plugins.BasicTextFormatter.prototype.isSilentCommand.call(
  229. null, command);
  230. assertEquals(shouldBeSilent, isSilent);
  231. }
  232. }
  233. function setUpSubSuperTests() {
  234. goog.dom.setTextContent(ROOT, '12345');
  235. HELPER = new goog.testing.editor.TestHelper(ROOT);
  236. HELPER.setUpEditableElement();
  237. }
  238. function tearDownSubSuperTests() {
  239. tearDownHelper();
  240. }
  241. function testSubscriptRemovesSuperscript() {
  242. setUpSubSuperTests();
  243. FIELDMOCK.$replay();
  244. HELPER.select('12345', 1, '12345', 4); // Selects '234'.
  245. FORMATTER.execCommandInternal(
  246. goog.editor.plugins.BasicTextFormatter.COMMAND.SUPERSCRIPT);
  247. HELPER.assertHtmlMatches('1' + OPEN_SUPER + '234' + CLOSE_SUPER + '5');
  248. FORMATTER.execCommandInternal(
  249. goog.editor.plugins.BasicTextFormatter.COMMAND.SUBSCRIPT);
  250. HELPER.assertHtmlMatches('1' + OPEN_SUB + '234' + CLOSE_SUB + '5');
  251. FIELDMOCK.$verify();
  252. tearDownSubSuperTests();
  253. }
  254. function testSuperscriptRemovesSubscript() {
  255. setUpSubSuperTests();
  256. FIELDMOCK.$replay();
  257. HELPER.select('12345', 1, '12345', 4); // Selects '234'.
  258. FORMATTER.execCommandInternal(
  259. goog.editor.plugins.BasicTextFormatter.COMMAND.SUBSCRIPT);
  260. HELPER.assertHtmlMatches('1' + OPEN_SUB + '234' + CLOSE_SUB + '5');
  261. FORMATTER.execCommandInternal(
  262. goog.editor.plugins.BasicTextFormatter.COMMAND.SUPERSCRIPT);
  263. HELPER.assertHtmlMatches('1' + OPEN_SUPER + '234' + CLOSE_SUPER + '5');
  264. FIELDMOCK.$verify();
  265. tearDownSubSuperTests();
  266. }
  267. function testSubscriptRemovesSuperscriptIntersecting() {
  268. // Tests: 12345 , sup(23) , sub(34) ==> 1+sup(2)+sub(34)+5
  269. // This is more complex because the sub and sup calls are made on separate
  270. // fields which intersect each other and queryCommandValue seems to return
  271. // false if the command is only applied to part of the field.
  272. setUpSubSuperTests();
  273. FIELDMOCK.$replay();
  274. HELPER.select('12345', 1, '12345', 3); // Selects '23'.
  275. FORMATTER.execCommandInternal(
  276. goog.editor.plugins.BasicTextFormatter.COMMAND.SUPERSCRIPT);
  277. HELPER.assertHtmlMatches('1' + OPEN_SUPER + '23' + CLOSE_SUPER + '45');
  278. HELPER.select('23', 1, '45', 1); // Selects '34'.
  279. FORMATTER.execCommandInternal(
  280. goog.editor.plugins.BasicTextFormatter.COMMAND.SUBSCRIPT);
  281. HELPER.assertHtmlMatches(
  282. '1' + OPEN_SUPER + '2' + CLOSE_SUPER + OPEN_SUB + '34' + CLOSE_SUB + '5');
  283. FIELDMOCK.$verify();
  284. tearDownSubSuperTests();
  285. }
  286. function testSuperscriptRemovesSubscriptIntersecting() {
  287. // Tests: 12345 , sub(23) , sup(34) ==> 1+sub(2)+sup(34)+5
  288. setUpSubSuperTests();
  289. FIELDMOCK.$replay();
  290. HELPER.select('12345', 1, '12345', 3); // Selects '23'.
  291. FORMATTER.execCommandInternal(
  292. goog.editor.plugins.BasicTextFormatter.COMMAND.SUBSCRIPT);
  293. HELPER.assertHtmlMatches('1' + OPEN_SUB + '23' + CLOSE_SUB + '45');
  294. HELPER.select('23', 1, '45', 1); // Selects '34'.
  295. FORMATTER.execCommandInternal(
  296. goog.editor.plugins.BasicTextFormatter.COMMAND.SUPERSCRIPT);
  297. HELPER.assertHtmlMatches(
  298. '1' + OPEN_SUB + '2' + CLOSE_SUB + OPEN_SUPER + '34' + CLOSE_SUPER + '5');
  299. FIELDMOCK.$verify();
  300. tearDownSubSuperTests();
  301. }
  302. function setUpLinkTests(text, url, isEditable) {
  303. stubs.set(window, 'prompt', function() { return url; });
  304. ROOT.innerHTML = text;
  305. HELPER = new goog.testing.editor.TestHelper(ROOT);
  306. if (isEditable) {
  307. HELPER.setUpEditableElement();
  308. FIELDMOCK
  309. .execCommand(
  310. goog.editor.Command.MODAL_LINK_EDITOR,
  311. goog.testing.mockmatchers.isObject)
  312. .$returns(undefined);
  313. FORMATTER.focusField_ = function() {
  314. throw 'Field should not be re-focused';
  315. };
  316. }
  317. FIELDMOCK.getElement().$anyTimes().$returns(ROOT);
  318. FIELDMOCK.setModalMode(true);
  319. FIELDMOCK.isSelectionEditable().$anyTimes().$returns(isEditable);
  320. }
  321. function tearDownLinkTests() {
  322. tearDownHelper();
  323. }
  324. function testLink() {
  325. setUpLinkTests('12345', 'http://www.x.com/', true);
  326. FIELDMOCK.$replay();
  327. HELPER.select('12345', 3);
  328. FORMATTER.execCommandInternal(goog.editor.Command.LINK);
  329. HELPER.assertHtmlMatches(
  330. goog.editor.BrowserFeature.GETS_STUCK_IN_LINKS ?
  331. '123<a href="http://www.x.com/">http://www.x.com/</a>&nbsp;45' :
  332. '123<a href="http://www.x.com/">http://www.x.com/</a>45');
  333. FIELDMOCK.$verify();
  334. tearDownLinkTests();
  335. }
  336. function testLinks() {
  337. var url1 = 'http://google.com/1';
  338. var url2 = 'http://google.com/2';
  339. var dialogUrl = 'http://google.com/3';
  340. var html = '<p>' + url1 + '</p><p>' + url2 + '</p>';
  341. setUpLinkTests(html, dialogUrl, true);
  342. FIELDMOCK.$replay();
  343. HELPER.select(url1, 0, url2, url2.length);
  344. FORMATTER.execCommandInternal(goog.editor.Command.LINK);
  345. var expectDialogUrl = false;
  346. if (goog.userAgent.IE ||
  347. (goog.userAgent.EDGE && !goog.userAgent.product.isVersion(14))) {
  348. expectDialogUrl = true;
  349. }
  350. HELPER.assertHtmlMatches(
  351. '<p><a href="' + url1 + '">' + url1 + '</a></p><p>' +
  352. '<a href="' + dialogUrl + '">' + (expectDialogUrl ? dialogUrl : url2) +
  353. '</a></p>');
  354. }
  355. function testSelectedLink() {
  356. setUpLinkTests('12345', 'http://www.x.com/', true);
  357. FIELDMOCK.$replay();
  358. HELPER.select('12345', 1, '12345', 4);
  359. FORMATTER.execCommandInternal(goog.editor.Command.LINK);
  360. HELPER.assertHtmlMatches(
  361. goog.editor.BrowserFeature.GETS_STUCK_IN_LINKS ?
  362. '1<a href="http://www.x.com/">234</a>&nbsp;5' :
  363. '1<a href="http://www.x.com/">234</a>5');
  364. FIELDMOCK.$verify();
  365. tearDownLinkTests();
  366. }
  367. function testCanceledLink() {
  368. setUpLinkTests('12345', undefined, true);
  369. FIELDMOCK.$replay();
  370. HELPER.select('12345', 1, '12345', 4);
  371. FORMATTER.execCommandInternal(goog.editor.Command.LINK);
  372. HELPER.assertHtmlMatches('12345');
  373. assertEquals('234', FIELDMOCK.getRange().getText());
  374. FIELDMOCK.$verify();
  375. tearDownLinkTests();
  376. }
  377. function testUnfocusedLink() {
  378. FIELDMOCK.$reset();
  379. FIELDMOCK.getEditableDomHelper().$anyTimes().$returns(
  380. goog.dom.getDomHelper(window.document));
  381. setUpLinkTests('12345', undefined, false);
  382. FIELDMOCK.getRange().$anyTimes().$returns(null);
  383. FIELDMOCK.$replay();
  384. FORMATTER.execCommandInternal(goog.editor.Command.LINK);
  385. HELPER.assertHtmlMatches('12345');
  386. FIELDMOCK.$verify();
  387. tearDownLinkTests();
  388. }
  389. function testCreateLink() {
  390. var text = 'some text here';
  391. var url = 'http://google.com';
  392. ROOT.innerHTML = text;
  393. HELPER = new goog.testing.editor.TestHelper(ROOT);
  394. HELPER.setUpEditableElement();
  395. FIELDMOCK.isSelectionEditable().$anyTimes().$returns(true);
  396. FIELDMOCK.getElement().$anyTimes().$returns(ROOT);
  397. FIELDMOCK.$replay();
  398. HELPER.select(text, 0, text, text.length);
  399. FORMATTER.execCommandInternal(
  400. goog.editor.plugins.BasicTextFormatter.COMMAND.CREATE_LINK,
  401. FIELDMOCK.getRange(), url);
  402. HELPER.assertHtmlMatches('<a href="' + url + '">' + text + '</a>');
  403. FIELDMOCK.$verify();
  404. tearDownLinkTests();
  405. }
  406. function setUpJustifyTests(html) {
  407. ROOT.innerHTML = html;
  408. HELPER = new goog.testing.editor.TestHelper(ROOT);
  409. HELPER.setUpEditableElement(ROOT);
  410. FIELDMOCK.getElement();
  411. FIELDMOCK.$anyTimes();
  412. FIELDMOCK.$returns(ROOT);
  413. FIELDMOCK.getElement();
  414. FIELDMOCK.$anyTimes();
  415. FIELDMOCK.$returns(ROOT);
  416. }
  417. function tearDownJustifyTests() {
  418. tearDownHelper();
  419. }
  420. function testJustify() {
  421. setUpJustifyTests('<div>abc</div><p>def</p><div>ghi</div>');
  422. FIELDMOCK.$replay();
  423. HELPER.select('abc', 1, 'def', 1); // Selects 'bcd'.
  424. FORMATTER.execCommandInternal(
  425. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_CENTER);
  426. HELPER.assertHtmlMatches(
  427. '<div style="text-align: center">abc</div>' +
  428. '<p style="text-align: center">def</p>' +
  429. '<div>ghi</div>');
  430. FIELDMOCK.$verify();
  431. tearDownJustifyTests();
  432. }
  433. function testJustifyInInline() {
  434. setUpJustifyTests('<div>a<i>b</i>c</div><div>d</div>');
  435. FIELDMOCK.$replay();
  436. HELPER.select('b', 0, 'b', 1); // Selects 'b'.
  437. FORMATTER.execCommandInternal(
  438. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_CENTER);
  439. HELPER.assertHtmlMatches(
  440. '<div style="text-align: center">a<i>b</i>c</div><div>d</div>');
  441. FIELDMOCK.$verify();
  442. tearDownJustifyTests();
  443. }
  444. function testJustifyInBlock() {
  445. setUpJustifyTests('<div>a<div>b</div>c</div>');
  446. FIELDMOCK.$replay();
  447. HELPER.select('b', 0, 'b', 1); // Selects 'h'.
  448. FORMATTER.execCommandInternal(
  449. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_CENTER);
  450. HELPER.assertHtmlMatches(
  451. '<div>a<div style="text-align: center">b</div>c</div>');
  452. FIELDMOCK.$verify();
  453. tearDownJustifyTests();
  454. }
  455. var isFontSizeTest = false;
  456. var defaultFontSizeMap;
  457. function setUpFontSizeTests() {
  458. isFontSizeTest = true;
  459. ROOT.innerHTML = '1<span style="font-size:2px">23</span>4' +
  460. '<span style="font-size:5px; white-space:pre">56</span>7';
  461. HELPER = new goog.testing.editor.TestHelper(ROOT);
  462. HELPER.setUpEditableElement();
  463. FIELDMOCK.getElement().$returns(ROOT).$anyTimes();
  464. // Map representing the sizes of the text in the HTML snippet used in these
  465. // tests. The key is the exact text content of each text node, and the value
  466. // is the initial size of the font in pixels. Since tests may cause a text
  467. // node to be split in two, this also contains keys that initially don't
  468. // match any text node, but may match one later if an existing node is
  469. // split. The value for these keys is null, signifying no text node should
  470. // exist with that content.
  471. defaultFontSizeMap = {
  472. '1': 16,
  473. '2': null,
  474. '23': 2,
  475. '3': null,
  476. '4': 16,
  477. '5': null,
  478. '56': 5,
  479. '6': null,
  480. '7': 16
  481. };
  482. assertFontSizes('Assertion failed on default font sizes!', {});
  483. }
  484. function tearDownFontSizeTests() {
  485. if (isFontSizeTest) {
  486. tearDownHelper();
  487. isFontSizeTest = false;
  488. }
  489. }
  490. /**
  491. * Asserts that the text nodes set up by setUpFontSizeTests() have had their
  492. * font sizes changed as described by sizeChangesMap.
  493. * @param {string} msg Assertion error message.
  494. * @param {Object<string,?number>} sizeChangesMap Maps the text content
  495. * of a text node to be measured to its expected font size in pixels, or
  496. * null if that text node should not be present in the document (i.e.
  497. * because it was split into two). Only the text nodes that have changed
  498. * from their default need to be specified.
  499. */
  500. function assertFontSizes(msg, sizeChangesMap) {
  501. goog.object.extend(defaultFontSizeMap, sizeChangesMap);
  502. for (var k in defaultFontSizeMap) {
  503. var node = HELPER.findTextNode(k);
  504. var expected = defaultFontSizeMap[k];
  505. if (expected) {
  506. assertNotNull(msg + ' [couldn\'t find text node "' + k + '"]', node);
  507. assertEquals(
  508. msg + ' [incorrect font size for "' + k + '"]', expected,
  509. goog.style.getFontSize(node.parentNode));
  510. } else {
  511. assertNull(msg + ' [unexpected text node "' + k + '"]', node);
  512. }
  513. }
  514. }
  515. /**
  516. * Regression test for {@bug 1286408}. Tests that when you change the font
  517. * size of a selection, any font size styles that were nested inside are
  518. * removed.
  519. */
  520. function testFontSizeOverridesStyleAttr() {
  521. setUpFontSizeTests();
  522. FIELDMOCK.$replay();
  523. HELPER.select('1', 0, '4', 1); // Selects 1234.
  524. FORMATTER.execCommandInternal(
  525. goog.editor.plugins.BasicTextFormatter.COMMAND.FONT_SIZE, 6);
  526. assertFontSizes(
  527. 'New font size should override existing font size',
  528. {'1': 32, '23': 32, '4': 32});
  529. if (goog.editor.BrowserFeature.DOESNT_OVERRIDE_FONT_SIZE_IN_STYLE_ATTR) {
  530. var span = HELPER.findTextNode('23').parentNode;
  531. assertFalse(
  532. 'Style attribute should be gone',
  533. span.getAttributeNode('style') != null &&
  534. span.getAttributeNode('style').specified);
  535. }
  536. FIELDMOCK.$verify();
  537. }
  538. /**
  539. * Make sure the style stripping works when the selection starts and stops in
  540. * different nodes that both contain font size styles.
  541. */
  542. function testFontSizeOverridesStyleAttrMultiNode() {
  543. setUpFontSizeTests();
  544. FIELDMOCK.$replay();
  545. HELPER.select('23', 0, '56', 2); // Selects 23456.
  546. FORMATTER.execCommandInternal(
  547. goog.editor.plugins.BasicTextFormatter.COMMAND.FONT_SIZE, 6);
  548. var span = HELPER.findTextNode('23').parentNode;
  549. var span2 = HELPER.findTextNode('56').parentNode;
  550. assertFontSizes(
  551. 'New font size should override existing font size in all spans',
  552. {'23': 32, '4': 32, '56': 32});
  553. var whiteSpace = goog.userAgent.IE ?
  554. goog.style.getCascadedStyle(span2, 'whiteSpace') :
  555. goog.style.getComputedStyle(span2, 'whiteSpace');
  556. assertEquals(
  557. 'Whitespace style in last span should have been left', 'pre', whiteSpace);
  558. if (goog.editor.BrowserFeature.DOESNT_OVERRIDE_FONT_SIZE_IN_STYLE_ATTR) {
  559. assertFalse(
  560. 'Style attribute should be gone from first span',
  561. span.getAttributeNode('style') != null &&
  562. span.getAttributeNode('style').specified);
  563. assertTrue(
  564. 'Style attribute should not be gone from last span',
  565. span2.getAttributeNode('style').specified);
  566. }
  567. FIELDMOCK.$verify();
  568. }
  569. /**
  570. * Makes sure the font size style is not removed when only a part of the
  571. * element with font size style is selected during the font size command.
  572. */
  573. function testFontSizeDoesntOverrideStyleAttr() {
  574. setUpFontSizeTests();
  575. FIELDMOCK.$replay();
  576. HELPER.select('23', 1, '4', 1); // Selects 34 (half of span with font size).
  577. FORMATTER.execCommandInternal(
  578. goog.editor.plugins.BasicTextFormatter.COMMAND.FONT_SIZE, 6);
  579. assertFontSizes(
  580. 'New font size shouldn\'t override existing font size before selection',
  581. {'2': 2, '23': null, '3': 32, '4': 32});
  582. FIELDMOCK.$verify();
  583. }
  584. /**
  585. * Makes sure the font size style is not removed when only a part of the
  586. * element with font size style is selected during the font size command, but
  587. * is removed for another element that is fully selected.
  588. */
  589. function testFontSizeDoesntOverrideStyleAttrMultiNode() {
  590. setUpFontSizeTests();
  591. FIELDMOCK.$replay();
  592. HELPER.select('23', 1, '56', 2); // Selects 3456.
  593. FORMATTER.execCommandInternal(
  594. goog.editor.plugins.BasicTextFormatter.COMMAND.FONT_SIZE, 6);
  595. assertFontSizes(
  596. 'New font size shouldn\'t override existing font size before ' +
  597. 'selection, but still override existing font size in last span',
  598. {'2': 2, '23': null, '3': 32, '4': 32, '56': 32});
  599. FIELDMOCK.$verify();
  600. }
  601. /**
  602. * Helper to make sure the precondition that executing the font size command
  603. * wraps the content in font tags instead of modifying the style attribute is
  604. * maintained by the browser even if the selection is already text that is
  605. * wrapped in a tag with a font size style. We test this with several
  606. * permutations of how the selection looks: selecting the text in the text
  607. * node, selecting the whole text node as a unit, or selecting the whole span
  608. * node as a unit. Sometimes the browser wraps the text node with the font
  609. * tag, sometimes it wraps the span with the font tag. Either one is ok as
  610. * long as a font tag is actually being used instead of just modifying the
  611. * span's style, because our fix for {@bug 1286408} would remove that style.
  612. * @param {function()} doSelect Function to select the "23" text in the test
  613. * content.
  614. */
  615. function doTestFontSizeStyledSpan(doSelect) {
  616. // Make sure no new browsers start getting this bad behavior. If they do,
  617. // this test will unexpectedly pass.
  618. expectedFailures.expectFailureFor(
  619. !goog.editor.BrowserFeature.DOESNT_OVERRIDE_FONT_SIZE_IN_STYLE_ATTR);
  620. try {
  621. setUpFontSizeTests();
  622. FIELDMOCK.$replay();
  623. doSelect();
  624. FORMATTER.execCommandInternal(
  625. goog.editor.plugins.BasicTextFormatter.COMMAND.FONT_SIZE, 7);
  626. var parentNode = HELPER.findTextNode('23').parentNode;
  627. var grandparentNode = parentNode.parentNode;
  628. var fontNode = goog.dom.getElementsByTagNameAndClass(
  629. goog.dom.TagName.FONT, undefined, ROOT)[0];
  630. var spanNode = goog.dom.getElementsByTagNameAndClass(
  631. goog.dom.TagName.SPAN, undefined, ROOT)[0];
  632. assertTrue(
  633. 'A font tag should have been added either outside or inside' +
  634. ' the existing span',
  635. parentNode == spanNode && grandparentNode == fontNode ||
  636. parentNode == fontNode && grandparentNode == spanNode);
  637. FIELDMOCK.$verify();
  638. } catch (e) {
  639. expectedFailures.handleException(e);
  640. }
  641. }
  642. function testFontSizeStyledSpanSelectingText() {
  643. doTestFontSizeStyledSpan(function() { HELPER.select('23', 0, '23', 2); });
  644. }
  645. function testFontSizeStyledSpanSelectingTextNode() {
  646. doTestFontSizeStyledSpan(function() {
  647. var textNode = HELPER.findTextNode('23');
  648. HELPER.select(textNode.parentNode, 0, textNode.parentNode, 1);
  649. });
  650. }
  651. function testFontSizeStyledSpanSelectingSpanNode() {
  652. doTestFontSizeStyledSpan(function() {
  653. var spanNode = HELPER.findTextNode('23').parentNode;
  654. HELPER.select(spanNode.parentNode, 1, spanNode.parentNode, 2);
  655. });
  656. }
  657. function setUpIframeField(content) {
  658. var ifr = document.getElementById('iframe');
  659. var body = ifr.contentWindow.document.body;
  660. body.innerHTML = content;
  661. HELPER = new goog.testing.editor.TestHelper(body);
  662. HELPER.setUpEditableElement();
  663. FIELDMOCK = new goog.testing.editor.FieldMock(ifr.contentWindow);
  664. FIELDMOCK.getElement();
  665. FIELDMOCK.$anyTimes();
  666. FIELDMOCK.$returns(body);
  667. FIELDMOCK.queryCommandValue('rtl');
  668. FIELDMOCK.$anyTimes();
  669. FIELDMOCK.$returns(null);
  670. FORMATTER.fieldObject = FIELDMOCK;
  671. }
  672. function tearDownIframeField() {
  673. tearDownHelper();
  674. }
  675. function setUpConvertBreaksToDivTests() {
  676. ROOT.innerHTML = '<p>paragraph</p>one<br id="br1">two<br><br><br>three';
  677. HELPER = new goog.testing.editor.TestHelper(ROOT);
  678. HELPER.setUpEditableElement();
  679. FIELDMOCK.getElement();
  680. FIELDMOCK.$anyTimes();
  681. FIELDMOCK.$returns(ROOT);
  682. }
  683. function tearDownConvertBreaksToDivTests() {
  684. tearDownHelper();
  685. }
  686. /** @bug 1414941 */
  687. function testConvertBreaksToDivsKeepsP() {
  688. if (goog.editor.BrowserFeature.CAN_LISTIFY_BR) {
  689. return;
  690. }
  691. setUpConvertBreaksToDivTests();
  692. FIELDMOCK.$replay();
  693. HELPER.select('three', 0);
  694. FORMATTER.convertBreaksToDivs_();
  695. assertEquals(
  696. 'There should still be a <p> tag', 1,
  697. goog.dom.getElementsByTagName(goog.dom.TagName.P, FIELDMOCK.getElement())
  698. .length);
  699. var html = FIELDMOCK.getElement().innerHTML.toLowerCase();
  700. assertNotBadBrElements(html);
  701. assertNotContains(
  702. 'There should not be empty <div> elements', '<div><\/div>', html);
  703. FIELDMOCK.$verify();
  704. tearDownConvertBreaksToDivTests();
  705. }
  706. /**
  707. * @bug 1414937
  708. * @bug 934535
  709. */
  710. function testConvertBreaksToDivsDoesntCollapseBR() {
  711. if (goog.editor.BrowserFeature.CAN_LISTIFY_BR) {
  712. return;
  713. }
  714. setUpConvertBreaksToDivTests();
  715. FIELDMOCK.$replay();
  716. HELPER.select('three', 0);
  717. FORMATTER.convertBreaksToDivs_();
  718. var html = FIELDMOCK.getElement().innerHTML.toLowerCase();
  719. assertNotBadBrElements(html);
  720. assertNotContains(
  721. 'There should not be empty <div> elements', '<div><\/div>', html);
  722. if (goog.userAgent.IE) {
  723. // <div><br></div> misbehaves in IE
  724. assertNotContains(
  725. '<br> should not be used to prevent <div> from collapsing',
  726. '<div><br><\/div>', html);
  727. }
  728. FIELDMOCK.$verify();
  729. tearDownConvertBreaksToDivTests();
  730. }
  731. function testConvertBreaksToDivsSelection() {
  732. if (goog.editor.BrowserFeature.CAN_LISTIFY_BR) {
  733. return;
  734. }
  735. setUpConvertBreaksToDivTests();
  736. FIELDMOCK.$replay();
  737. HELPER.select('two', 1, 'three', 3);
  738. var before = FIELDMOCK.getRange().getText().replace(/\s/g, '');
  739. FORMATTER.convertBreaksToDivs_();
  740. assertEquals(
  741. 'Selection must not be changed', before,
  742. FIELDMOCK.getRange().getText().replace(/\s/g, ''));
  743. FIELDMOCK.$verify();
  744. tearDownConvertBreaksToDivTests();
  745. }
  746. /** @bug 1414937 */
  747. function testConvertBreaksToDivsInsertList() {
  748. setUpConvertBreaksToDivTests();
  749. FIELDMOCK.$replay();
  750. HELPER.select('three', 0);
  751. FORMATTER.execCommandInternal('insertorderedlist');
  752. assertTrue(
  753. 'Ordered list must be inserted',
  754. FIELDMOCK.getEditableDomHelper().getDocument().queryCommandState(
  755. 'insertorderedlist'));
  756. tearDownConvertBreaksToDivTests();
  757. }
  758. /**
  759. * Regression test for {@bug 1939883}, where if a br has an id, it causes
  760. * the convert br code to throw a js error. This goes a step further and
  761. * ensures that the id is preserved in the resulting div element.
  762. */
  763. function testConvertBreaksToDivsKeepsId() {
  764. if (goog.editor.BrowserFeature.CAN_LISTIFY_BR) {
  765. return;
  766. }
  767. setUpConvertBreaksToDivTests();
  768. FIELDMOCK.$replay();
  769. HELPER.select('one', 0, 'two', 0);
  770. FORMATTER.convertBreaksToDivs_();
  771. var html = FIELDMOCK.getElement().innerHTML.toLowerCase();
  772. assertNotBadBrElements(html);
  773. var idBr = document.getElementById('br1');
  774. assertNotNull('There should still be a tag with id="br1"', idBr);
  775. assertEquals('The tag with id="br1" should be a <div> now',
  776. String(goog.dom.TagName.DIV), idBr.tagName);
  777. assertNull(
  778. 'There should not be any tag with id="temp_br"',
  779. document.getElementById('temp_br'));
  780. FIELDMOCK.$verify();
  781. tearDownConvertBreaksToDivTests();
  782. }
  783. /**
  784. * @bug 2420054
  785. */
  786. var JUSTIFICATION_COMMANDS = [
  787. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT,
  788. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT,
  789. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_CENTER,
  790. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_FULL
  791. ];
  792. function doTestIsJustification(command) {
  793. setUpRealField();
  794. REAL_FIELD.setHtml(false, 'foo');
  795. selectRealField();
  796. REAL_FIELD.execCommand(command);
  797. for (var i = 0; i < JUSTIFICATION_COMMANDS.length; i++) {
  798. if (JUSTIFICATION_COMMANDS[i] == command) {
  799. assertTrue(
  800. 'queryCommandValue(' + JUSTIFICATION_COMMANDS[i] +
  801. ') should be true after execCommand(' + command + ')',
  802. REAL_FIELD.queryCommandValue(JUSTIFICATION_COMMANDS[i]));
  803. } else {
  804. assertFalse(
  805. 'queryCommandValue(' + JUSTIFICATION_COMMANDS[i] +
  806. ') should be false after execCommand(' + command + ')',
  807. REAL_FIELD.queryCommandValue(JUSTIFICATION_COMMANDS[i]));
  808. }
  809. }
  810. }
  811. function testIsJustificationLeft() {
  812. doTestIsJustification(
  813. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT);
  814. }
  815. function testIsJustificationRight() {
  816. doTestIsJustification(
  817. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT);
  818. }
  819. function testIsJustificationCenter() {
  820. doTestIsJustification(
  821. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_CENTER);
  822. }
  823. function testIsJustificationFull() {
  824. doTestIsJustification(
  825. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_FULL);
  826. }
  827. /**
  828. * Regression test for {@bug 1414813}, where all 3 justification buttons are
  829. * considered "on" when you first tab into the editable field. In this
  830. * situation, when lorem ipsum is the only node in the editable field iframe
  831. * body, mockField.getRange() returns an empty range.
  832. */
  833. function testIsJustificationEmptySelection() {
  834. var mockField = new goog.testing.LooseMock(goog.editor.Field);
  835. mockField.getRange();
  836. mockField.$anyTimes();
  837. mockField.$returns(null);
  838. mockField.getPluginByClassId('Bidi');
  839. mockField.$anyTimes();
  840. mockField.$returns(null);
  841. FORMATTER.fieldObject = mockField;
  842. mockField.$replay();
  843. assertFalse(
  844. 'Empty selection should not be justified',
  845. FORMATTER.isJustification_(
  846. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_CENTER));
  847. assertFalse(
  848. 'Empty selection should not be justified',
  849. FORMATTER.isJustification_(
  850. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_FULL));
  851. assertFalse(
  852. 'Empty selection should not be justified',
  853. FORMATTER.isJustification_(
  854. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT));
  855. assertFalse(
  856. 'Empty selection should not be justified',
  857. FORMATTER.isJustification_(
  858. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT));
  859. mockField.$verify();
  860. }
  861. function testIsJustificationSimple1() {
  862. setUpRealField();
  863. REAL_FIELD.setHtml(false, '<div align="right">foo</div>');
  864. selectRealField();
  865. assertFalse(
  866. REAL_FIELD.queryCommandValue(
  867. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT));
  868. assertTrue(
  869. REAL_FIELD.queryCommandValue(
  870. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT));
  871. }
  872. function testIsJustificationSimple2() {
  873. setUpRealField();
  874. REAL_FIELD.setHtml(false, '<div style="text-align: right;">foo</div>');
  875. selectRealField();
  876. assertFalse(
  877. REAL_FIELD.queryCommandValue(
  878. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT));
  879. assertTrue(
  880. REAL_FIELD.queryCommandValue(
  881. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT));
  882. }
  883. function testIsJustificationComplete1() {
  884. setUpRealField();
  885. REAL_FIELD.setHtml(
  886. false, '<div align="left">a</div><div align="right">b</div>');
  887. selectRealField();
  888. assertFalse(
  889. REAL_FIELD.queryCommandValue(
  890. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT));
  891. assertFalse(
  892. REAL_FIELD.queryCommandValue(
  893. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT));
  894. }
  895. function testIsJustificationComplete2() {
  896. setUpRealField();
  897. REAL_FIELD.setHtml(
  898. false, '<div align="left">a</div><div align="left">b</div>');
  899. selectRealField();
  900. assertTrue(
  901. REAL_FIELD.queryCommandValue(
  902. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT));
  903. assertFalse(
  904. REAL_FIELD.queryCommandValue(
  905. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT));
  906. }
  907. function testIsJustificationComplete3() {
  908. setUpRealField();
  909. REAL_FIELD.setHtml(
  910. false, '<div align="right">a</div><div align="right">b</div>');
  911. selectRealField();
  912. assertFalse(
  913. REAL_FIELD.queryCommandValue(
  914. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT));
  915. assertTrue(
  916. REAL_FIELD.queryCommandValue(
  917. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT));
  918. }
  919. function testIsJustificationComplete4() {
  920. setUpRealField();
  921. REAL_FIELD.setHtml(
  922. false, '<div align="right"><div align="left">a</div></div>' +
  923. '<div align="right">b</div>');
  924. selectRealField();
  925. assertFalse(
  926. REAL_FIELD.queryCommandValue(
  927. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT));
  928. assertTrue(
  929. REAL_FIELD.queryCommandValue(
  930. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT));
  931. }
  932. function testIsJustificationComplete5() {
  933. setUpRealField();
  934. REAL_FIELD.setHtml(
  935. false, '<div align="right">a</div>b' +
  936. '<div align="right">c</div>');
  937. selectRealField();
  938. assertFalse(
  939. REAL_FIELD.queryCommandValue(
  940. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT));
  941. assertFalse(
  942. REAL_FIELD.queryCommandValue(
  943. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT));
  944. }
  945. /** @bug 2472589 */
  946. function doTestIsJustificationPInDiv(useCss, align, command) {
  947. setUpRealField();
  948. var html = '<div ' + (useCss ? 'style="text-align:' : 'align="') + align +
  949. '"><p>foo</p></div>';
  950. REAL_FIELD.setHtml(false, html);
  951. selectRealField();
  952. assertTrue(
  953. 'P inside ' + align + ' aligned' + (useCss ? ' (using CSS)' : '') +
  954. ' DIV should be considered ' + align + ' aligned',
  955. REAL_FIELD.queryCommandValue(command));
  956. }
  957. function testIsJustificationPInDivLeft() {
  958. doTestIsJustificationPInDiv(
  959. false, 'left',
  960. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT);
  961. }
  962. function testIsJustificationPInDivRight() {
  963. doTestIsJustificationPInDiv(
  964. false, 'right',
  965. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT);
  966. }
  967. function testIsJustificationPInDivCenter() {
  968. doTestIsJustificationPInDiv(
  969. false, 'center',
  970. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_CENTER);
  971. }
  972. function testIsJustificationPInDivJustify() {
  973. doTestIsJustificationPInDiv(
  974. false, 'justify',
  975. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_FULL);
  976. }
  977. function testIsJustificationPInDivLeftCss() {
  978. doTestIsJustificationPInDiv(
  979. true, 'left',
  980. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT);
  981. }
  982. function testIsJustificationPInDivRightCss() {
  983. doTestIsJustificationPInDiv(
  984. true, 'right',
  985. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT);
  986. }
  987. function testIsJustificationPInDivCenterCss() {
  988. doTestIsJustificationPInDiv(
  989. true, 'center',
  990. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_CENTER);
  991. }
  992. function testIsJustificationPInDivJustifyCss() {
  993. doTestIsJustificationPInDiv(
  994. true, 'justify',
  995. goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_FULL);
  996. }
  997. function testPrepareContent() {
  998. setUpRealField();
  999. assertPreparedContents('\n', '\n');
  1000. if (goog.editor.BrowserFeature.COLLAPSES_EMPTY_NODES) {
  1001. assertPreparedContents(
  1002. "&nbsp;<script>alert('hi')<" + '/script>',
  1003. "<script>alert('hi')<" + '/script>');
  1004. } else {
  1005. assertNotPreparedContents("<script>alert('hi')<" + '/script>');
  1006. }
  1007. if (goog.editor.BrowserFeature.CONVERT_TO_B_AND_I_TAGS) {
  1008. assertPreparedContents(
  1009. '<b id=\'foo\'>hi</b>', '<strong id=\'foo\'>hi</strong>');
  1010. assertPreparedContents('<i>hi</i>', '<em>hi</em>');
  1011. assertNotPreparedContents('<embed>');
  1012. } else {
  1013. assertNotPreparedContents('<em>hi</em>');
  1014. assertNotPreparedContents('<strong id=\'foo\'>hi</strong>');
  1015. }
  1016. }
  1017. function testScrubImagesRemovesCustomAttributes() {
  1018. var fieldElem = goog.dom.getElement('real-field');
  1019. goog.dom.removeChildren(fieldElem);
  1020. var attrs = {
  1021. 'src': 'http://www.google.com/foo.jpg',
  1022. 'tabIndex': '0',
  1023. 'tabIndexSet': '0'
  1024. };
  1025. attrs[goog.HASH_CODE_PROPERTY_] = '0';
  1026. goog.dom.appendChild(
  1027. fieldElem, goog.dom.createDom(goog.dom.TagName.IMG, attrs));
  1028. setUpRealField();
  1029. var html = REAL_FIELD.getCleanContents();
  1030. assert(
  1031. 'Images must not have forbidden attributes: ' + html,
  1032. -1 == html.indexOf('tabIndex') && -1 == html.indexOf('closure'));
  1033. assert(
  1034. 'Image URLs must not be made relative by default: ' + html,
  1035. -1 != html.indexOf('/foo.jpg'));
  1036. }
  1037. function testGeckoSelectionChange() {
  1038. if (!goog.userAgent.GECKO || !goog.userAgent.isVersionOrHigher('1.9')) {
  1039. return;
  1040. }
  1041. setUpRealFieldIframe();
  1042. // Use native selection for this test because goog.dom.Range will
  1043. // change selections of <br>
  1044. var ifr = document.getElementById('iframe');
  1045. ifr.contentWindow.document.body.innerHTML = 'hello<br id="br1"><br id="br2">';
  1046. var body = ifr.contentWindow.document.body;
  1047. var range = REAL_FIELD.getRange();
  1048. var browserRange = range.getBrowserRangeObject();
  1049. browserRange.setStart(body, 2);
  1050. browserRange.setEnd(body, 2);
  1051. FORMATTER.applyExecCommandGeckoFixes_('formatblock');
  1052. var updatedRange = REAL_FIELD.getRange().getBrowserRangeObject();
  1053. assertEquals(
  1054. 'Range should have been updated to deep range', 'br2',
  1055. updatedRange.startContainer.id);
  1056. assertEquals(
  1057. 'Range should have been updated to deep range', 0,
  1058. updatedRange.startOffset);
  1059. }
  1060. function testIEExecCommandFixes() {
  1061. if (!goog.userAgent.IE) {
  1062. return;
  1063. }
  1064. setUpRealField();
  1065. REAL_FIELD.setHtml(false, '<blockquote>hi</blockquote>');
  1066. goog.dom.Range.createFromNodeContents(REAL_FIELD.getElement()).select();
  1067. var nodes = REAL_PLUGIN.applyExecCommandIEFixes_('insertOrderedList');
  1068. if (goog.userAgent.isVersionOrHigher('9')) {
  1069. assertHTMLEquals(
  1070. '<blockquote>hi<div style="height:0px"></div></blockquote>',
  1071. REAL_FIELD.getCleanContents());
  1072. } else {
  1073. assertHTMLEquals(
  1074. '<blockquote>hi <div style="height:0px"></div></blockquote>',
  1075. REAL_FIELD.getCleanContents());
  1076. }
  1077. }
  1078. /**
  1079. * Assert that the prepared contents matches the expected.
  1080. */
  1081. function assertPreparedContents(expected, original) {
  1082. assertEquals(
  1083. expected, REAL_FIELD.reduceOp_(
  1084. goog.editor.Plugin.Op.PREPARE_CONTENTS_HTML, original));
  1085. }
  1086. /**
  1087. * Assert that sanitization doesn't affect the given text.
  1088. */
  1089. function assertNotPreparedContents(text) {
  1090. assertPreparedContents(text, text);
  1091. }
  1092. /**
  1093. * Assert that only BR elements expected to persist after convertBreaksToDivs_
  1094. * are in the HTML.
  1095. */
  1096. function assertNotBadBrElements(html) {
  1097. if (goog.userAgent.IE) {
  1098. assertNotContains('There should not be <br> elements', '<br', html);
  1099. } else {
  1100. assertFalse(
  1101. 'There should not be <br> elements, except ones to prevent ' +
  1102. '<div>s from collapsing' + html,
  1103. /(?!<div>)<br>(?!<\/div>)/.test(html));
  1104. }
  1105. }