linkdialogplugin_test.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740
  1. // Copyright 2010 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.ui.editor.plugins.LinkDialogTest');
  15. goog.setTestOnly('goog.ui.editor.plugins.LinkDialogTest');
  16. goog.require('goog.dom');
  17. goog.require('goog.dom.DomHelper');
  18. goog.require('goog.dom.NodeType');
  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.Link');
  24. goog.require('goog.editor.plugins.LinkDialogPlugin');
  25. goog.require('goog.string');
  26. goog.require('goog.string.Unicode');
  27. goog.require('goog.testing.MockControl');
  28. goog.require('goog.testing.editor.FieldMock');
  29. goog.require('goog.testing.editor.TestHelper');
  30. goog.require('goog.testing.editor.dom');
  31. goog.require('goog.testing.events');
  32. goog.require('goog.testing.jsunit');
  33. goog.require('goog.testing.mockmatchers');
  34. goog.require('goog.ui.editor.AbstractDialog');
  35. goog.require('goog.ui.editor.LinkDialog');
  36. goog.require('goog.userAgent');
  37. var plugin;
  38. var anchorElem;
  39. var extraAnchors;
  40. var isNew;
  41. var testDiv;
  42. var mockCtrl;
  43. var mockField;
  44. var mockLink;
  45. var mockAlert;
  46. var OLD_LINK_TEXT = 'old text';
  47. var OLD_LINK_URL = 'http://old.url/';
  48. var NEW_LINK_TEXT = 'My Link Text';
  49. var NEW_LINK_URL = 'http://my.link/url/';
  50. var fieldElem;
  51. var fieldObj;
  52. var linkObj;
  53. function setUp() {
  54. testDiv = goog.dom.getDocument().getElementById('test');
  55. goog.dom.setTextContent(testDiv, 'Some preceding text');
  56. anchorElem = goog.dom.createElement(goog.dom.TagName.A);
  57. anchorElem.href = 'http://www.google.com/';
  58. goog.dom.setTextContent(anchorElem, 'anchor text');
  59. goog.dom.appendChild(testDiv, anchorElem);
  60. extraAnchors = [];
  61. mockCtrl = new goog.testing.MockControl();
  62. mockField = new goog.testing.editor.FieldMock();
  63. mockCtrl.addMock(mockField);
  64. mockLink = mockCtrl.createLooseMock(goog.editor.Link);
  65. mockAlert = mockCtrl.createGlobalFunctionMock('alert');
  66. isNew = false;
  67. mockLink.isNew().$anyTimes().$does(function() { return isNew; });
  68. mockLink
  69. .setTextAndUrl(
  70. goog.testing.mockmatchers.isString,
  71. goog.testing.mockmatchers.isString)
  72. .$anyTimes()
  73. .$does(function(text, url) {
  74. anchorElem.innerHTML = text;
  75. anchorElem.href = url;
  76. });
  77. mockLink.getAnchor().$anyTimes().$returns(anchorElem);
  78. mockLink.getExtraAnchors().$anyTimes().$returns(extraAnchors);
  79. }
  80. function tearDown() {
  81. plugin.dispose();
  82. tearDownRealEditableField();
  83. goog.dom.removeChildren(testDiv);
  84. mockCtrl.$tearDown();
  85. }
  86. function setUpAnchor(text, href, opt_isNew, opt_target, opt_rel) {
  87. setUpGivenAnchor(anchorElem, text, href, opt_isNew, opt_target, opt_rel);
  88. }
  89. function setUpGivenAnchor(anchor, text, href, opt_isNew, opt_target, opt_rel) {
  90. anchor.innerHTML = text;
  91. anchor.href = href;
  92. isNew = !!opt_isNew;
  93. if (opt_target) {
  94. anchor.target = opt_target;
  95. }
  96. if (opt_rel) {
  97. anchor.rel = opt_rel;
  98. }
  99. }
  100. /**
  101. * Tests that the plugin's dialog is properly created.
  102. */
  103. function testCreateDialog() {
  104. // Note: this tests simply creating the dialog because that's the only
  105. // functionality added to this class. Opening or closing effects (editing
  106. // the actual link) is tested in linkdialog_test.html, but should be moved
  107. // here if that functionality gets refactored from the dialog to the plugin.
  108. mockCtrl.$replayAll();
  109. plugin = new goog.editor.plugins.LinkDialogPlugin();
  110. plugin.registerFieldObject(mockField);
  111. var dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink);
  112. assertTrue(
  113. 'Dialog should be of type goog.ui.editor.LinkDialog',
  114. dialog instanceof goog.ui.editor.LinkDialog);
  115. mockCtrl.$verifyAll();
  116. }
  117. /**
  118. * Tests that when the OK event fires the link is properly updated.
  119. */
  120. function testOk() {
  121. mockLink.placeCursorRightOf();
  122. mockField.dispatchSelectionChangeEvent();
  123. mockField.dispatchChange();
  124. mockField.focus();
  125. mockCtrl.$replayAll();
  126. setUpAnchor(OLD_LINK_TEXT, OLD_LINK_URL);
  127. plugin = new goog.editor.plugins.LinkDialogPlugin();
  128. plugin.registerFieldObject(mockField);
  129. var dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink);
  130. // Mock of execCommand + clicking OK without actually opening the dialog.
  131. plugin.currentLink_ = mockLink;
  132. dialog.dispatchEvent(
  133. new goog.ui.editor.LinkDialog.OkEvent(NEW_LINK_TEXT, NEW_LINK_URL));
  134. assertEquals('Display text incorrect', NEW_LINK_TEXT, anchorElem.innerHTML);
  135. assertEquals('Anchor element href incorrect', NEW_LINK_URL, anchorElem.href);
  136. mockCtrl.$verifyAll();
  137. }
  138. /**
  139. * Tests that when the Cancel event fires the link is unchanged.
  140. */
  141. function testCancel() {
  142. mockCtrl.$replayAll();
  143. setUpAnchor(OLD_LINK_TEXT, OLD_LINK_URL);
  144. plugin = new goog.editor.plugins.LinkDialogPlugin();
  145. plugin.registerFieldObject(mockField);
  146. var dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink);
  147. // Mock of execCommand + cancel without actually opening the dialog.
  148. plugin.currentLink_ = mockLink;
  149. dialog.dispatchEvent(goog.ui.editor.AbstractDialog.EventType.CANCEL);
  150. assertEquals(
  151. 'Display text should not be changed', OLD_LINK_TEXT,
  152. anchorElem.innerHTML);
  153. assertEquals(
  154. 'Anchor element href should not be changed', OLD_LINK_URL,
  155. anchorElem.href);
  156. mockCtrl.$verifyAll();
  157. }
  158. /**
  159. * Tests that when the Cancel event fires for a new link it gets removed.
  160. */
  161. function testCancelNew() {
  162. mockField.dispatchChange(); // Should be fired because link was removed.
  163. mockCtrl.$replayAll();
  164. setUpAnchor(OLD_LINK_TEXT, OLD_LINK_URL, true);
  165. var prevSib = anchorElem.previousSibling;
  166. plugin = new goog.editor.plugins.LinkDialogPlugin();
  167. plugin.registerFieldObject(mockField);
  168. var dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink);
  169. // Mock of execCommand + cancel without actually opening the dialog.
  170. plugin.currentLink_ = mockLink;
  171. dialog.dispatchEvent(goog.ui.editor.AbstractDialog.EventType.CANCEL);
  172. assertNotEquals(
  173. 'Anchor element should be removed from document body', testDiv,
  174. anchorElem.parentNode);
  175. var newElem = prevSib.nextSibling;
  176. assertEquals(
  177. 'Link should be replaced by text node', goog.dom.NodeType.TEXT,
  178. newElem.nodeType);
  179. assertEquals(
  180. 'Original text should be left behind', OLD_LINK_TEXT, newElem.nodeValue);
  181. mockCtrl.$verifyAll();
  182. }
  183. /**
  184. * Tests that when the Cancel event fires for a new link it gets removed.
  185. */
  186. function testCancelNewMultiple() {
  187. mockField.dispatchChange(); // Should be fired because link was removed.
  188. mockCtrl.$replayAll();
  189. var anchorElem1 = anchorElem;
  190. var parent1 = goog.dom.createDom(goog.dom.TagName.DIV, null, anchorElem1);
  191. goog.dom.appendChild(testDiv, parent1);
  192. setUpGivenAnchor(anchorElem1, OLD_LINK_TEXT + '1', OLD_LINK_URL + '1', true);
  193. anchorElem2 = goog.dom.createDom(goog.dom.TagName.A);
  194. var parent2 = goog.dom.createDom(goog.dom.TagName.DIV, null, anchorElem2);
  195. goog.dom.appendChild(testDiv, parent2);
  196. setUpGivenAnchor(anchorElem2, OLD_LINK_TEXT + '2', OLD_LINK_URL + '2', true);
  197. extraAnchors.push(anchorElem2);
  198. anchorElem3 = goog.dom.createDom(goog.dom.TagName.A);
  199. var parent3 = goog.dom.createDom(goog.dom.TagName.DIV, null, anchorElem3);
  200. goog.dom.appendChild(testDiv, parent3);
  201. setUpGivenAnchor(anchorElem3, OLD_LINK_TEXT + '3', OLD_LINK_URL + '3', true);
  202. extraAnchors.push(anchorElem3);
  203. plugin = new goog.editor.plugins.LinkDialogPlugin();
  204. plugin.registerFieldObject(mockField);
  205. var dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink);
  206. // Mock of execCommand + cancel without actually opening the dialog.
  207. plugin.currentLink_ = mockLink;
  208. dialog.dispatchEvent(goog.ui.editor.AbstractDialog.EventType.CANCEL);
  209. assertNotEquals(
  210. 'Anchor 1 element should be removed from document body', parent1,
  211. anchorElem1.parentNode);
  212. assertNotEquals(
  213. 'Anchor 2 element should be removed from document body', parent2,
  214. anchorElem2.parentNode);
  215. assertNotEquals(
  216. 'Anchor 3 element should be removed from document body', parent3,
  217. anchorElem3.parentNode);
  218. assertEquals(
  219. 'Link 1 should be replaced by text node', goog.dom.NodeType.TEXT,
  220. parent1.firstChild.nodeType);
  221. assertEquals(
  222. 'Link 2 should be replaced by text node', goog.dom.NodeType.TEXT,
  223. parent2.firstChild.nodeType);
  224. assertEquals(
  225. 'Link 3 should be replaced by text node', goog.dom.NodeType.TEXT,
  226. parent3.firstChild.nodeType);
  227. assertEquals(
  228. 'Original text 1 should be left behind', OLD_LINK_TEXT + '1',
  229. parent1.firstChild.nodeValue);
  230. assertEquals(
  231. 'Original text 2 should be left behind', OLD_LINK_TEXT + '2',
  232. parent2.firstChild.nodeValue);
  233. assertEquals(
  234. 'Original text 3 should be left behind', OLD_LINK_TEXT + '3',
  235. parent3.firstChild.nodeValue);
  236. mockCtrl.$verifyAll();
  237. }
  238. /**
  239. * Tests that when the Cancel event fires for a new link it gets removed.
  240. */
  241. function testOkNewMultiple() {
  242. mockLink.placeCursorRightOf();
  243. mockField.dispatchSelectionChangeEvent();
  244. mockField.dispatchChange();
  245. mockField.focus();
  246. mockCtrl.$replayAll();
  247. var anchorElem1 = anchorElem;
  248. setUpGivenAnchor(anchorElem1, OLD_LINK_TEXT + '1', OLD_LINK_URL + '1', true);
  249. anchorElem2 = goog.dom.createElement(goog.dom.TagName.A);
  250. goog.dom.appendChild(testDiv, anchorElem2);
  251. setUpGivenAnchor(anchorElem2, OLD_LINK_TEXT + '2', OLD_LINK_URL + '2', true);
  252. extraAnchors.push(anchorElem2);
  253. anchorElem3 = goog.dom.createElement(goog.dom.TagName.A);
  254. goog.dom.appendChild(testDiv, anchorElem3);
  255. setUpGivenAnchor(anchorElem3, OLD_LINK_TEXT + '3', OLD_LINK_URL + '3', true);
  256. extraAnchors.push(anchorElem3);
  257. var prevSib = anchorElem1.previousSibling;
  258. plugin = new goog.editor.plugins.LinkDialogPlugin();
  259. plugin.registerFieldObject(mockField);
  260. var dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink);
  261. // Mock of execCommand + clicking OK without actually opening the dialog.
  262. plugin.currentLink_ = mockLink;
  263. dialog.dispatchEvent(
  264. new goog.ui.editor.LinkDialog.OkEvent(NEW_LINK_TEXT, NEW_LINK_URL));
  265. assertEquals(
  266. 'Display text 1 must update', NEW_LINK_TEXT, anchorElem1.innerHTML);
  267. assertEquals(
  268. 'Display text 2 must not update', OLD_LINK_TEXT + '2',
  269. anchorElem2.innerHTML);
  270. assertEquals(
  271. 'Display text 3 must not update', OLD_LINK_TEXT + '3',
  272. anchorElem3.innerHTML);
  273. assertEquals(
  274. 'Anchor element 1 href must update', NEW_LINK_URL, anchorElem1.href);
  275. assertEquals(
  276. 'Anchor element 2 href must update', NEW_LINK_URL, anchorElem2.href);
  277. assertEquals(
  278. 'Anchor element 3 href must update', NEW_LINK_URL, anchorElem3.href);
  279. mockCtrl.$verifyAll();
  280. }
  281. /**
  282. * Tests the anchor's target is correctly modified with the "open in new
  283. * window" feature on.
  284. */
  285. function testOkOpenInNewWindow() {
  286. mockLink.placeCursorRightOf().$anyTimes();
  287. mockField.dispatchSelectionChangeEvent().$anyTimes();
  288. mockField.dispatchChange().$anyTimes();
  289. mockField.focus().$anyTimes();
  290. mockCtrl.$replayAll();
  291. plugin = new goog.editor.plugins.LinkDialogPlugin();
  292. plugin.registerFieldObject(mockField);
  293. plugin.showOpenLinkInNewWindow(false);
  294. plugin.currentLink_ = mockLink;
  295. // Edit a link that doesn't open in a new window and leave it as such.
  296. setUpAnchor(OLD_LINK_TEXT, OLD_LINK_URL);
  297. var dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink);
  298. dialog.dispatchEvent(
  299. new goog.ui.editor.LinkDialog.OkEvent(
  300. NEW_LINK_TEXT, NEW_LINK_URL, false, false));
  301. assertEquals(
  302. 'Target should not be set for link that doesn\'t open in new window', '',
  303. anchorElem.target);
  304. assertFalse(
  305. 'Checked state should stay false',
  306. plugin.getOpenLinkInNewWindowCheckedState());
  307. // Edit a link that doesn't open in a new window and toggle it on.
  308. setUpAnchor(OLD_LINK_TEXT, OLD_LINK_URL);
  309. dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink);
  310. dialog.dispatchEvent(
  311. new goog.ui.editor.LinkDialog.OkEvent(NEW_LINK_TEXT, NEW_LINK_URL, true));
  312. assertEquals(
  313. 'Target should be set to _blank for link that opens in new window',
  314. '_blank', anchorElem.target);
  315. assertTrue(
  316. 'Checked state should be true after toggling a link on',
  317. plugin.getOpenLinkInNewWindowCheckedState());
  318. // Edit a link that doesn't open in a named window and don't touch it.
  319. setUpAnchor(OLD_LINK_TEXT, OLD_LINK_URL, false, 'named');
  320. dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink);
  321. dialog.dispatchEvent(
  322. new goog.ui.editor.LinkDialog.OkEvent(
  323. NEW_LINK_TEXT, NEW_LINK_URL, false));
  324. assertEquals(
  325. 'Target should keep its original value', 'named', anchorElem.target);
  326. assertFalse(
  327. 'Checked state should be false again',
  328. plugin.getOpenLinkInNewWindowCheckedState());
  329. // Edit a link that opens in a new window and toggle it off.
  330. setUpAnchor(OLD_LINK_TEXT, OLD_LINK_URL, false, '_blank');
  331. dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink);
  332. dialog.dispatchEvent(
  333. new goog.ui.editor.LinkDialog.OkEvent(
  334. NEW_LINK_TEXT, NEW_LINK_URL, false));
  335. assertEquals(
  336. 'Target should not be set for link that doesn\'t open in new window', '',
  337. anchorElem.target);
  338. mockCtrl.$verifyAll();
  339. }
  340. function testOkNoFollowEnabled() {
  341. verifyRelNoFollow(true, null, 'nofollow');
  342. }
  343. function testOkNoFollowInUppercase() {
  344. verifyRelNoFollow(true, 'NOFOLLOW', 'NOFOLLOW');
  345. }
  346. function testOkNoFollowEnabledHasMoreRelValues() {
  347. verifyRelNoFollow(true, 'author', 'author nofollow');
  348. }
  349. function testOkNoFollowDisabled() {
  350. verifyRelNoFollow(false, null, '');
  351. }
  352. function testOkNoFollowDisabledHasMoreRelValues() {
  353. verifyRelNoFollow(false, 'author', 'author');
  354. }
  355. function testOkNoFollowDisabledHasMoreRelValues() {
  356. verifyRelNoFollow(false, 'author nofollow', 'author ');
  357. }
  358. function testOkNoFollowInUppercaseWithMoreValues() {
  359. verifyRelNoFollow(true, 'NOFOLLOW author', 'NOFOLLOW author');
  360. }
  361. function verifyRelNoFollow(noFollow, originalRel, expectedRel) {
  362. mockLink.placeCursorRightOf();
  363. mockField.dispatchSelectionChangeEvent();
  364. mockField.dispatchChange();
  365. mockField.focus();
  366. mockCtrl.$replayAll();
  367. plugin = new goog.editor.plugins.LinkDialogPlugin();
  368. plugin.registerFieldObject(mockField);
  369. plugin.showRelNoFollow();
  370. plugin.currentLink_ = mockLink;
  371. setUpAnchor(OLD_LINK_TEXT, OLD_LINK_URL, true, null, originalRel);
  372. var dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink);
  373. dialog.dispatchEvent(
  374. new goog.ui.editor.LinkDialog.OkEvent(
  375. NEW_LINK_TEXT, NEW_LINK_URL, false, noFollow));
  376. assertEquals(expectedRel, anchorElem.rel);
  377. mockCtrl.$verifyAll();
  378. }
  379. /**
  380. * Tests that the selection is cleared when the dialog opens and is
  381. * correctly restored after cancel is clicked.
  382. */
  383. function testRestoreSelectionOnOk() {
  384. setUpAnchor('12345', '/');
  385. setUpRealEditableField();
  386. var elem = fieldObj.getElement();
  387. var helper = new goog.testing.editor.TestHelper(elem);
  388. helper.select('12345', 1, '12345', 4); // Selects '234'.
  389. assertEquals(
  390. 'Incorrect text selected before dialog is opened', '234',
  391. fieldObj.getRange().getText());
  392. plugin.execCommand(goog.editor.Command.MODAL_LINK_EDITOR, linkObj);
  393. if (!goog.userAgent.IE && !goog.userAgent.OPERA) {
  394. // IE returns some bogus range when field doesn't have selection.
  395. // You can't remove the selection from a whitebox field in Opera.
  396. assertNull(
  397. 'There should be no selection while dialog is open',
  398. fieldObj.getRange());
  399. }
  400. goog.testing.events.fireClickSequence(plugin.dialog_.getOkButtonElement());
  401. assertEquals(
  402. 'No text should be selected after clicking ok', '',
  403. fieldObj.getRange().getText());
  404. // Test that the caret is placed at the end of the link text.
  405. goog.testing.editor.dom.assertRangeBetweenText(
  406. // If the browser gets stuck in links, an nbsp was added after the link
  407. // to avoid that, otherwise we just look for the 5.
  408. goog.editor.BrowserFeature.GETS_STUCK_IN_LINKS ?
  409. goog.string.Unicode.NBSP :
  410. '5',
  411. '', fieldObj.getRange());
  412. // NOTE(user): The functionality to avoid getting stuck in links is
  413. // tested in editablelink_test.html::testPlaceCursorRightOf().
  414. }
  415. /**
  416. * Tests that the selection is cleared when the dialog opens and is
  417. * correctly restored after cancel is clicked.
  418. * @param {boolean=} opt_isNew Whether to test behavior when creating a new
  419. * link (cancelling will flatten it).
  420. */
  421. function testRestoreSelectionOnCancel(opt_isNew) {
  422. setUpAnchor('12345', '/', opt_isNew);
  423. setUpRealEditableField();
  424. var elem = fieldObj.getElement();
  425. var helper = new goog.testing.editor.TestHelper(elem);
  426. helper.select('12345', 1, '12345', 4); // Selects '234'.
  427. assertEquals(
  428. 'Incorrect text selected before dialog is opened', '234',
  429. fieldObj.getRange().getText());
  430. plugin.execCommand(goog.editor.Command.MODAL_LINK_EDITOR, linkObj);
  431. if (!goog.userAgent.IE && !goog.userAgent.OPERA) {
  432. // IE returns some bogus range when field doesn't have selection.
  433. // You can't remove the selection from a whitebox field in Opera.
  434. assertNull(
  435. 'There should be no selection while dialog is open',
  436. fieldObj.getRange());
  437. }
  438. goog.testing.events.fireClickSequence(
  439. plugin.dialog_.getCancelButtonElement());
  440. assertEquals(
  441. 'Incorrect text selected after clicking cancel', '234',
  442. fieldObj.getRange().getText());
  443. }
  444. /**
  445. * Tests that the selection is cleared when the dialog opens and is
  446. * correctly restored after cancel is clicked and the new link is removed.
  447. */
  448. function testRestoreSelectionOnCancelNew() {
  449. testRestoreSelectionOnCancel(true);
  450. }
  451. /**
  452. * Tests that the BeforeTestLink event is suppressed for invalid url schemes.
  453. */
  454. function testTestLinkDisabledForInvalidScheme() {
  455. mockAlert(goog.testing.mockmatchers.isString);
  456. mockCtrl.$replayAll();
  457. var invalidUrl = 'javascript:document.write(\'hello\');';
  458. plugin = new goog.editor.plugins.LinkDialogPlugin();
  459. var dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink);
  460. // Mock of execCommand + clicking test without actually opening the dialog.
  461. var dispatched = dialog.dispatchEvent(
  462. new goog.ui.editor.LinkDialog.BeforeTestLinkEvent(invalidUrl));
  463. assertFalse(dispatched);
  464. mockCtrl.$verifyAll();
  465. }
  466. function testIsSafeSchemeToOpen() {
  467. plugin = new goog.editor.plugins.LinkDialogPlugin();
  468. // Urls with no scheme at all are ok too since 'http://' will be prepended.
  469. var good = [
  470. 'http://google.com', 'http://google.com/', 'https://google.com',
  471. 'null@google.com', 'http://www.google.com', 'http://site.com', 'google.com',
  472. 'google', 'http://google', 'HTTP://GOOGLE.COM', 'HtTp://www.google.com'
  473. ];
  474. var bad = [
  475. 'javascript:google.com', 'httpp://google.com', 'data:foo',
  476. 'javascript:alert(\'hi\');', 'abc:def'
  477. ];
  478. for (var i = 0; i < good.length; i++) {
  479. assertTrue(
  480. good[i] + ' should have a safe scheme',
  481. plugin.isSafeSchemeToOpen_(good[i]));
  482. }
  483. for (i = 0; i < bad.length; i++) {
  484. assertFalse(
  485. bad[i] + ' should have an unsafe scheme',
  486. plugin.isSafeSchemeToOpen_(bad[i]));
  487. }
  488. }
  489. function testShouldOpenWithWhitelist() {
  490. plugin.setSafeToOpenSchemes(['abc']);
  491. assertTrue('Scheme should be safe', plugin.shouldOpenUrl('abc://google.com'));
  492. assertFalse(
  493. 'Scheme should be unsafe', plugin.shouldOpenUrl('http://google.com'));
  494. plugin.setBlockOpeningUnsafeSchemes(false);
  495. assertTrue(
  496. 'Non-whitelisted should now be safe after disabling blocking',
  497. plugin.shouldOpenUrl('http://google.com'));
  498. }
  499. /**
  500. * Regression test for http://b/issue?id=1607766 . Without the fix, this
  501. * should give an Invalid Argument error in IE, because the editable field
  502. * caches a selection util that has a reference to the node of the link text
  503. * before it is edited (which gets replaced by a new node for the new text
  504. * after editing).
  505. */
  506. function testBug1607766() {
  507. setUpAnchor('abc', 'def');
  508. setUpRealEditableField();
  509. var elem = fieldObj.getElement();
  510. var helper = new goog.testing.editor.TestHelper(elem);
  511. helper.select('abc', 1, 'abc', 2); // Selects 'b'.
  512. // Dispatching a selection event causes the field to cache a selection
  513. // util, which is the root of the bug.
  514. plugin.fieldObject.dispatchSelectionChangeEvent();
  515. plugin.execCommand(goog.editor.Command.MODAL_LINK_EDITOR, linkObj);
  516. goog.dom.getElement(goog.ui.editor.LinkDialog.Id_.TEXT_TO_DISPLAY).value =
  517. 'Abc';
  518. goog.testing.events.fireClickSequence(plugin.dialog_.getOkButtonElement());
  519. // In IE the unit test somehow doesn't cause a browser focus event, so we
  520. // need to manually invoke this, which is where the bug happens.
  521. plugin.fieldObject.dispatchFocus_();
  522. }
  523. /**
  524. * Regression test for http://b/issue?id=2215546 .
  525. */
  526. function testBug2215546() {
  527. setUpRealEditableField();
  528. var elem = fieldObj.getElement();
  529. fieldObj.setHtml(false, '<div><a href="/"></a></div>');
  530. anchorElem = elem.firstChild.firstChild;
  531. linkObj = new goog.editor.Link(anchorElem, true);
  532. var helper = new goog.testing.editor.TestHelper(elem);
  533. // Select "</a>" in a way, simulating what IE does if you hit enter twice,
  534. // arrow up into the blank line and open the link dialog.
  535. helper.select(anchorElem, 0, elem.firstChild, 1);
  536. plugin.execCommand(goog.editor.Command.MODAL_LINK_EDITOR, linkObj);
  537. goog.dom.getElement(goog.ui.editor.LinkDialog.Id_.TEXT_TO_DISPLAY).value =
  538. 'foo';
  539. goog.dom.getElement(goog.ui.editor.LinkDialog.Id_.ON_WEB_INPUT).value = 'foo';
  540. var okButton = plugin.dialog_.getOkButtonElement();
  541. okButton.disabled = false;
  542. goog.testing.events.fireClickSequence(okButton);
  543. assertEquals(
  544. 'Link text should have been inserted', 'foo', anchorElem.innerHTML);
  545. }
  546. /**
  547. * Test that link insertion doesn't scroll the field to the top
  548. * after clicking Cancel or OK.
  549. */
  550. function testBug7279077ScrollOnFocus() {
  551. if (goog.userAgent.IE) {
  552. return; // TODO(user): take this out once b/7279077 fixed for IE too.
  553. }
  554. setUpAnchor('12345', '/');
  555. setUpRealEditableField();
  556. // Make the field scrollable and kinda small.
  557. var elem = fieldObj.getElement();
  558. elem.style.overflow = 'auto';
  559. elem.style.height = '40px';
  560. elem.style.width = '200px';
  561. elem.style.contenteditable = 'true';
  562. // Add a bunch of text before the anchor tag.
  563. var longTextElem = goog.dom.createElement(goog.dom.TagName.SPAN);
  564. longTextElem.innerHTML = goog.string.repeat('All work and no play.<p>', 20);
  565. elem.insertBefore(longTextElem, elem.firstChild);
  566. var helper = new goog.testing.editor.TestHelper(elem);
  567. helper.select('12345', 1, '12345', 4); // Selects '234'.
  568. // Scroll down.
  569. elem.scrollTop = 60;
  570. // Bring up the link insertion dialog, then cancel.
  571. plugin.execCommand(goog.editor.Command.MODAL_LINK_EDITOR, linkObj);
  572. goog.dom.getElement(goog.ui.editor.LinkDialog.Id_.TEXT_TO_DISPLAY).value =
  573. 'foo';
  574. goog.dom.getElement(goog.ui.editor.LinkDialog.Id_.ON_WEB_INPUT).value = 'foo';
  575. var cancelButton = plugin.dialog_.getCancelButtonElement();
  576. goog.testing.events.fireClickSequence(cancelButton);
  577. assertEquals(
  578. 'Field should not have scrolled after cancel', 60, elem.scrollTop);
  579. // Now let's try it with clicking the OK button.
  580. plugin.execCommand(goog.editor.Command.MODAL_LINK_EDITOR, linkObj);
  581. goog.dom.getElement(goog.ui.editor.LinkDialog.Id_.TEXT_TO_DISPLAY).value =
  582. 'foo';
  583. goog.dom.getElement(goog.ui.editor.LinkDialog.Id_.ON_WEB_INPUT).value = 'foo';
  584. var okButton = plugin.dialog_.getOkButtonElement();
  585. goog.testing.events.fireClickSequence(okButton);
  586. assertEquals('Field should not have scrolled after OK', 60, elem.scrollTop);
  587. }
  588. /**
  589. * Setup a real editable field (instead of a mock) and register the plugin to
  590. * it.
  591. */
  592. function setUpRealEditableField() {
  593. fieldElem = goog.dom.createElement(goog.dom.TagName.DIV);
  594. fieldElem.id = 'myField';
  595. document.body.appendChild(fieldElem);
  596. fieldElem.appendChild(anchorElem);
  597. fieldObj = new goog.editor.Field('myField', document);
  598. fieldObj.makeEditable();
  599. linkObj = new goog.editor.Link(fieldObj.getElement().firstChild, isNew);
  600. // Register the plugin to that field.
  601. plugin = new goog.editor.plugins.LinkDialogPlugin();
  602. fieldObj.registerPlugin(plugin);
  603. }
  604. /**
  605. * Tear down the real editable field.
  606. */
  607. function tearDownRealEditableField() {
  608. if (fieldObj) {
  609. fieldObj.makeUneditable();
  610. fieldObj.dispose();
  611. fieldObj = null;
  612. }
  613. goog.dom.removeNode(fieldElem);
  614. }