abstractdialog_test.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  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.ui.editor.AbstractDialogTest');
  15. goog.setTestOnly('goog.ui.editor.AbstractDialogTest');
  16. goog.require('goog.dom');
  17. goog.require('goog.dom.DomHelper');
  18. goog.require('goog.dom.TagName');
  19. goog.require('goog.dom.classlist');
  20. goog.require('goog.events.Event');
  21. goog.require('goog.events.EventHandler');
  22. goog.require('goog.events.KeyCodes');
  23. goog.require('goog.testing.MockControl');
  24. goog.require('goog.testing.events');
  25. goog.require('goog.testing.jsunit');
  26. goog.require('goog.testing.mockmatchers.ArgumentMatcher');
  27. goog.require('goog.ui.editor.AbstractDialog');
  28. goog.require('goog.userAgent');
  29. function shouldRunTests() {
  30. // Test disabled in IE7 due to flakiness. See b/4269021.
  31. return !(goog.userAgent.IE && goog.userAgent.isVersionOrHigher('7'));
  32. }
  33. var dialog;
  34. var builder;
  35. var mockCtrl;
  36. var mockAfterHideHandler;
  37. var mockOkHandler;
  38. var mockCancelHandler;
  39. var mockCustomButtonHandler;
  40. var CUSTOM_EVENT = 'customEvent';
  41. var CUSTOM_BUTTON_ID = 'customButton';
  42. function setUp() {
  43. mockCtrl = new goog.testing.MockControl();
  44. mockAfterHideHandler = mockCtrl.createLooseMock(goog.events.EventHandler);
  45. mockOkHandler = mockCtrl.createLooseMock(goog.events.EventHandler);
  46. mockCancelHandler = mockCtrl.createLooseMock(goog.events.EventHandler);
  47. mockCustomButtonHandler = mockCtrl.createLooseMock(goog.events.EventHandler);
  48. }
  49. function tearDown() {
  50. if (dialog) {
  51. mockAfterHideHandler.$setIgnoreUnexpectedCalls(true);
  52. dialog.dispose();
  53. }
  54. }
  55. /**
  56. * Sets up the mock event handler to expect an AFTER_HIDE event.
  57. */
  58. function expectAfterHide() {
  59. mockAfterHideHandler.handleEvent(
  60. new goog.testing.mockmatchers.ArgumentMatcher(function(arg) {
  61. return arg.type == goog.ui.editor.AbstractDialog.EventType.AFTER_HIDE;
  62. }));
  63. }
  64. /**
  65. * Sets up the mock event handler to expect an OK event.
  66. */
  67. function expectOk() {
  68. mockOkHandler.handleEvent(
  69. new goog.testing.mockmatchers.ArgumentMatcher(function(arg) {
  70. return arg.type == goog.ui.editor.AbstractDialog.EventType.OK;
  71. }));
  72. }
  73. /**
  74. * Sets up the mock event handler to expect an OK event and to call
  75. * preventDefault when handling it.
  76. */
  77. function expectOkPreventDefault() {
  78. expectOk();
  79. mockOkHandler.$does(function(e) { e.preventDefault(); });
  80. }
  81. /**
  82. * Sets up the mock event handler to expect an OK event and to return false
  83. * when handling it.
  84. */
  85. function expectOkReturnFalse() {
  86. expectOk();
  87. mockOkHandler.$returns(false);
  88. }
  89. /**
  90. * Sets up the mock event handler to expect a CANCEL event.
  91. */
  92. function expectCancel() {
  93. mockCancelHandler.handleEvent(
  94. new goog.testing.mockmatchers.ArgumentMatcher(function(arg) {
  95. return arg.type == goog.ui.editor.AbstractDialog.EventType.CANCEL;
  96. }));
  97. }
  98. /**
  99. * Sets up the mock event handler to expect a custom button event.
  100. */
  101. function expectCustomButton() {
  102. mockCustomButtonHandler.handleEvent(
  103. new goog.testing.mockmatchers.ArgumentMatcher(function(arg) {
  104. return arg.type == CUSTOM_EVENT;
  105. }));
  106. }
  107. /**
  108. * Helper to create the dialog being tested in each test. Since NewDialog is
  109. * abstract, needs to add a concrete version of any abstract methods. Also
  110. * creates up the global builder variable which should be set up after the call
  111. * to this method.
  112. * @return {goog.ui.editor.AbstractDialog} The dialog.
  113. */
  114. function createTestDialog() {
  115. var dialog = new goog.ui.editor.AbstractDialog(new goog.dom.DomHelper());
  116. builder = new goog.ui.editor.AbstractDialog.Builder(dialog);
  117. dialog.createDialogControl = function() { return builder.build(); };
  118. dialog.createOkEvent = function(e) {
  119. return new goog.events.Event(goog.ui.editor.AbstractDialog.EventType.OK);
  120. };
  121. dialog.addEventListener(
  122. goog.ui.editor.AbstractDialog.EventType.AFTER_HIDE, mockAfterHideHandler);
  123. dialog.addEventListener(
  124. goog.ui.editor.AbstractDialog.EventType.OK, mockOkHandler);
  125. dialog.addEventListener(
  126. goog.ui.editor.AbstractDialog.EventType.CANCEL, mockCancelHandler);
  127. dialog.addEventListener(CUSTOM_EVENT, mockCustomButtonHandler);
  128. return dialog;
  129. }
  130. /**
  131. * Asserts that the given dialog is open.
  132. * @param {string} msg Message to be printed in case of failure.
  133. * @param {goog.ui.editor.AbstractDialog} dialog Dialog to be tested.
  134. */
  135. function assertOpen(msg, dialog) {
  136. assertTrue(msg + ' [AbstractDialog.isOpen()]', dialog && dialog.isOpen());
  137. }
  138. /**
  139. * Asserts that the given dialog is closed.
  140. * @param {string} msg Message to be printed in case of failure.
  141. * @param {goog.ui.editor.AbstractDialog} dialog Dialog to be tested.
  142. */
  143. function assertNotOpen(msg, dialog) {
  144. assertFalse(msg + ' [AbstractDialog.isOpen()]', dialog && dialog.isOpen());
  145. }
  146. /**
  147. * Tests that if you create a dialog and hide it without having shown it, no
  148. * errors occur.
  149. */
  150. function testCreateAndHide() {
  151. dialog = createTestDialog();
  152. mockCtrl.$replayAll();
  153. assertNotOpen('Dialog should not be open after creation', dialog);
  154. dialog.hide();
  155. assertNotOpen('Dialog should not be open after hide()', dialog);
  156. mockCtrl.$verifyAll(); // Verifies AFTER_HIDE was not dispatched.
  157. }
  158. /**
  159. * Tests that when you show and hide a dialog the flags indicating open are
  160. * correct and the AFTER_HIDE event is dispatched (and no errors happen).
  161. */
  162. function testShowAndHide() {
  163. dialog = createTestDialog();
  164. expectAfterHide(dialog);
  165. mockCtrl.$replayAll();
  166. assertNotOpen('Dialog should not be open before show()', dialog);
  167. dialog.show();
  168. assertOpen('Dialog should be open after show()', dialog);
  169. dialog.hide();
  170. assertNotOpen('Dialog should not be open after hide()', dialog);
  171. mockCtrl.$verifyAll(); // Verifies AFTER_HIDE was dispatched.
  172. }
  173. /**
  174. * Tests that when you show and dispose a dialog (without hiding it first) the
  175. * flags indicating open are correct and the AFTER_HIDE event is dispatched (and
  176. * no errors happen).
  177. */
  178. function testShowAndDispose() {
  179. dialog = createTestDialog();
  180. expectAfterHide(dialog);
  181. mockCtrl.$replayAll();
  182. assertNotOpen('Dialog should not be open before show()', dialog);
  183. dialog.show();
  184. assertOpen('Dialog should be open after show()', dialog);
  185. dialog.dispose();
  186. assertNotOpen('Dialog should not be open after dispose()', dialog);
  187. mockCtrl.$verifyAll(); // Verifies AFTER_HIDE was dispatched.
  188. }
  189. /**
  190. * Tests that when you dispose a dialog (without ever showing it first) the
  191. * flags indicating open are correct and the AFTER_HIDE event is never
  192. * dispatched (and no errors happen).
  193. */
  194. function testDisposeWithoutShow() {
  195. dialog = createTestDialog();
  196. mockCtrl.$replayAll();
  197. assertNotOpen('Dialog should not be open before dispose()', dialog);
  198. dialog.dispose();
  199. assertNotOpen('Dialog should not be open after dispose()', dialog);
  200. mockCtrl.$verifyAll(); // Verifies AFTER_HIDE was NOT dispatched.
  201. }
  202. /**
  203. * Tests that labels set in the builder can be found in the resulting dialog's
  204. * HTML.
  205. */
  206. function testBasicLayout() {
  207. dialog = createTestDialog();
  208. mockCtrl.$replayAll();
  209. // create some dialog content
  210. var content = goog.dom.createDom(goog.dom.TagName.DIV, null, 'The Content');
  211. builder.setTitle('The Title')
  212. .setContent(content)
  213. .addOkButton('The OK Button')
  214. .addCancelButton()
  215. .addButton('The Apply Button', goog.nullFunction)
  216. .addClassName('myClassName');
  217. dialog.show();
  218. var dialogElem = dialog.dialogInternal_.getElement();
  219. var html = dialogElem.innerHTML;
  220. // TODO(user): This is really insufficient. If the title and content
  221. // were swapped this test would still pass!
  222. assertContains('Dialog html should contain title', '>The Title<', html);
  223. assertContains('Dialog html should contain content', '>The Content<', html);
  224. assertContains(
  225. 'Dialog html should contain custom OK button label', '>The OK Button<',
  226. html);
  227. assertContains(
  228. 'Dialog html should contain default Cancel button label', '>Cancel<',
  229. html);
  230. assertContains(
  231. 'Dialog html should contain custom button label', '>The Apply Button<',
  232. html);
  233. assertTrue(
  234. 'Dialog should have default Closure class',
  235. goog.dom.classlist.contains(dialogElem, 'modal-dialog'));
  236. assertTrue(
  237. 'Dialog should have our custom class',
  238. goog.dom.classlist.contains(dialogElem, 'myClassName'));
  239. mockCtrl.$verifyAll();
  240. }
  241. /**
  242. * Tests that clicking the OK button dispatches the OK event and closes the
  243. * dialog (dispatching the AFTER_HIDE event too).
  244. */
  245. function testOk() {
  246. dialog = createTestDialog();
  247. expectOk(dialog);
  248. expectAfterHide(dialog);
  249. mockCtrl.$replayAll();
  250. dialog.show();
  251. goog.testing.events.fireClickSequence(dialog.getOkButtonElement());
  252. assertNotOpen('Dialog should not be open after clicking OK', dialog);
  253. mockCtrl.$verifyAll();
  254. }
  255. /**
  256. * Tests that hitting the enter key dispatches the OK event and closes the
  257. * dialog (dispatching the AFTER_HIDE event too).
  258. */
  259. function testEnter() {
  260. dialog = createTestDialog();
  261. expectOk(dialog);
  262. expectAfterHide(dialog);
  263. mockCtrl.$replayAll();
  264. dialog.show();
  265. goog.testing.events.fireKeySequence(
  266. dialog.dialogInternal_.getElement(), goog.events.KeyCodes.ENTER);
  267. assertNotOpen('Dialog should not be open after hitting enter', dialog);
  268. mockCtrl.$verifyAll();
  269. }
  270. /**
  271. * Tests that clicking the Cancel button dispatches the CANCEL event and closes
  272. * the dialog (dispatching the AFTER_HIDE event too).
  273. */
  274. function testCancel() {
  275. dialog = createTestDialog();
  276. expectCancel(dialog);
  277. expectAfterHide(dialog);
  278. mockCtrl.$replayAll();
  279. builder.addCancelButton('My Cancel Button');
  280. dialog.show();
  281. goog.testing.events.fireClickSequence(dialog.getCancelButtonElement());
  282. assertNotOpen('Dialog should not be open after clicking Cancel', dialog);
  283. mockCtrl.$verifyAll();
  284. }
  285. /**
  286. * Tests that hitting the escape key dispatches the CANCEL event and closes
  287. * the dialog (dispatching the AFTER_HIDE event too).
  288. */
  289. function testEscape() {
  290. dialog = createTestDialog();
  291. expectCancel(dialog);
  292. expectAfterHide(dialog);
  293. mockCtrl.$replayAll();
  294. dialog.show();
  295. goog.testing.events.fireKeySequence(
  296. dialog.dialogInternal_.getElement(), goog.events.KeyCodes.ESC);
  297. assertNotOpen('Dialog should not be open after hitting escape', dialog);
  298. mockCtrl.$verifyAll();
  299. }
  300. /**
  301. * Tests that clicking the custom button dispatches the custom event and closes
  302. * the dialog (dispatching the AFTER_HIDE event too).
  303. */
  304. function testCustomButton() {
  305. dialog = createTestDialog();
  306. expectCustomButton(dialog);
  307. expectAfterHide(dialog);
  308. mockCtrl.$replayAll();
  309. builder.addButton('My Custom Button', function() {
  310. dialog.dispatchEvent(CUSTOM_EVENT);
  311. }, CUSTOM_BUTTON_ID);
  312. dialog.show();
  313. goog.testing.events.fireClickSequence(
  314. dialog.getButtonElement(CUSTOM_BUTTON_ID));
  315. assertNotOpen(
  316. 'Dialog should not be open after clicking custom button', dialog);
  317. mockCtrl.$verifyAll();
  318. }
  319. /**
  320. * Tests that if the OK handler calls preventDefault, the dialog doesn't close.
  321. */
  322. function testOkPreventDefault() {
  323. dialog = createTestDialog();
  324. expectOkPreventDefault(dialog);
  325. mockCtrl.$replayAll();
  326. dialog.show();
  327. goog.testing.events.fireClickSequence(dialog.getOkButtonElement());
  328. assertOpen(
  329. 'Dialog should not be closed because preventDefault was called', dialog);
  330. mockCtrl.$verifyAll();
  331. }
  332. /**
  333. * Tests that if the OK handler returns false, the dialog doesn't close.
  334. */
  335. function testOkReturnFalse() {
  336. dialog = createTestDialog();
  337. expectOkReturnFalse(dialog);
  338. mockCtrl.$replayAll();
  339. dialog.show();
  340. goog.testing.events.fireClickSequence(dialog.getOkButtonElement());
  341. assertOpen(
  342. 'Dialog should not be closed because handler returned false', dialog);
  343. mockCtrl.$verifyAll();
  344. }
  345. /**
  346. * Tests that if creating the OK event fails, no event is dispatched and the
  347. * dialog doesn't close.
  348. */
  349. function testCreateOkEventFail() {
  350. dialog = createTestDialog();
  351. dialog.createOkEvent = function() { // Override our mock createOkEvent.
  352. return null;
  353. };
  354. mockCtrl.$replayAll();
  355. dialog.show();
  356. goog.testing.events.fireClickSequence(dialog.getOkButtonElement());
  357. assertOpen(
  358. 'Dialog should not be closed because OK event creation failed', dialog);
  359. mockCtrl.$verifyAll(); // Verifies that no event was dispatched.
  360. }
  361. /**
  362. * Tests that processOkAndClose() dispatches the OK event and closes the
  363. * dialog (dispatching the AFTER_HIDE event too).
  364. */
  365. function testProcessOkAndClose() {
  366. dialog = createTestDialog();
  367. expectOk(dialog);
  368. expectAfterHide(dialog);
  369. mockCtrl.$replayAll();
  370. dialog.show();
  371. dialog.processOkAndClose();
  372. assertNotOpen('Dialog should not be open after processOkAndClose()', dialog);
  373. mockCtrl.$verifyAll();
  374. }
  375. /**
  376. * Tests that if the OK handler triggered by processOkAndClose calls
  377. * preventDefault, the dialog doesn't close (in the old implementation this
  378. * failed due to not great design, so this is sort of a regression test).
  379. */
  380. function testProcessOkAndClosePreventDefault() {
  381. dialog = createTestDialog();
  382. expectOkPreventDefault(dialog);
  383. mockCtrl.$replayAll();
  384. dialog.show();
  385. dialog.processOkAndClose();
  386. assertOpen(
  387. 'Dialog should not be closed because preventDefault was called', dialog);
  388. mockCtrl.$verifyAll();
  389. }