checkbox_test.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. // Copyright 2009 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. goog.provide('goog.ui.CheckboxTest');
  15. goog.setTestOnly('goog.ui.CheckboxTest');
  16. goog.require('goog.a11y.aria');
  17. goog.require('goog.a11y.aria.Role');
  18. goog.require('goog.a11y.aria.State');
  19. goog.require('goog.dom');
  20. goog.require('goog.dom.TagName');
  21. goog.require('goog.dom.classlist');
  22. goog.require('goog.events');
  23. goog.require('goog.events.KeyCodes');
  24. goog.require('goog.testing.events');
  25. goog.require('goog.testing.jsunit');
  26. goog.require('goog.ui.Checkbox');
  27. goog.require('goog.ui.CheckboxRenderer');
  28. goog.require('goog.ui.Component');
  29. goog.require('goog.ui.ControlRenderer');
  30. goog.require('goog.ui.decorate');
  31. var checkbox;
  32. function setUp() {
  33. checkbox = new goog.ui.Checkbox();
  34. }
  35. function tearDown() {
  36. checkbox.dispose();
  37. }
  38. function testClassNames() {
  39. checkbox.createDom();
  40. checkbox.setChecked(false);
  41. assertSameElements(
  42. 'classnames of unchecked checkbox',
  43. ['goog-checkbox', 'goog-checkbox-unchecked'],
  44. goog.dom.classlist.get(checkbox.getElement()));
  45. checkbox.setChecked(true);
  46. assertSameElements(
  47. 'classnames of checked checkbox',
  48. ['goog-checkbox', 'goog-checkbox-checked'],
  49. goog.dom.classlist.get(checkbox.getElement()));
  50. checkbox.setChecked(null);
  51. assertSameElements(
  52. 'classnames of partially checked checkbox',
  53. ['goog-checkbox', 'goog-checkbox-undetermined'],
  54. goog.dom.classlist.get(checkbox.getElement()));
  55. checkbox.setEnabled(false);
  56. assertSameElements(
  57. 'classnames of partially checked disabled checkbox',
  58. ['goog-checkbox', 'goog-checkbox-undetermined', 'goog-checkbox-disabled'],
  59. goog.dom.classlist.get(checkbox.getElement()));
  60. }
  61. function testIsEnabled() {
  62. assertTrue('enabled by default', checkbox.isEnabled());
  63. checkbox.setEnabled(false);
  64. assertFalse('has been disabled', checkbox.isEnabled());
  65. }
  66. function testCheckedState() {
  67. assertTrue(
  68. 'unchecked by default', !checkbox.isChecked() && checkbox.isUnchecked() &&
  69. !checkbox.isUndetermined());
  70. checkbox.setChecked(true);
  71. assertTrue(
  72. 'set to checked', checkbox.isChecked() && !checkbox.isUnchecked() &&
  73. !checkbox.isUndetermined());
  74. checkbox.setChecked(null);
  75. assertTrue(
  76. 'set to partially checked', !checkbox.isChecked() &&
  77. !checkbox.isUnchecked() && checkbox.isUndetermined());
  78. }
  79. function testToggle() {
  80. checkbox.setChecked(null);
  81. checkbox.toggle();
  82. assertTrue('undetermined -> checked', checkbox.getChecked());
  83. checkbox.toggle();
  84. assertFalse('checked -> unchecked', checkbox.getChecked());
  85. checkbox.toggle();
  86. assertTrue('unchecked -> checked', checkbox.getChecked());
  87. }
  88. function testEvents() {
  89. checkbox.render();
  90. var events = [];
  91. goog.events.listen(
  92. checkbox,
  93. [
  94. goog.ui.Component.EventType.ACTION, goog.ui.Component.EventType.CHECK,
  95. goog.ui.Component.EventType.UNCHECK, goog.ui.Component.EventType.CHANGE
  96. ],
  97. function(e) { events.push(e.type); });
  98. checkbox.setEnabled(false);
  99. goog.testing.events.fireClickSequence(checkbox.getElement());
  100. assertArrayEquals('disabled => no events', [], events);
  101. assertFalse('checked state did not change', checkbox.getChecked());
  102. events = [];
  103. checkbox.setEnabled(true);
  104. goog.testing.events.fireClickSequence(checkbox.getElement());
  105. assertArrayEquals(
  106. 'ACTION+CHECK+CHANGE fired',
  107. [
  108. goog.ui.Component.EventType.ACTION, goog.ui.Component.EventType.CHECK,
  109. goog.ui.Component.EventType.CHANGE
  110. ],
  111. events);
  112. assertTrue('checkbox became checked', checkbox.getChecked());
  113. events = [];
  114. goog.testing.events.fireClickSequence(checkbox.getElement());
  115. assertArrayEquals(
  116. 'ACTION+UNCHECK+CHANGE fired',
  117. [
  118. goog.ui.Component.EventType.ACTION, goog.ui.Component.EventType.UNCHECK,
  119. goog.ui.Component.EventType.CHANGE
  120. ],
  121. events);
  122. assertFalse('checkbox became unchecked', checkbox.getChecked());
  123. events = [];
  124. goog.events.listen(checkbox, goog.ui.Component.EventType.CHECK, function(e) {
  125. e.preventDefault();
  126. });
  127. goog.testing.events.fireClickSequence(checkbox.getElement());
  128. assertArrayEquals(
  129. 'ACTION+CHECK fired',
  130. [goog.ui.Component.EventType.ACTION, goog.ui.Component.EventType.CHECK],
  131. events);
  132. assertFalse('toggling has been prevented', checkbox.getChecked());
  133. }
  134. function testCheckboxAriaLabelledby() {
  135. var label = goog.dom.createElement(goog.dom.TagName.DIV);
  136. var label2 = goog.dom.createElement(
  137. goog.dom.TagName.DIV, {id: checkbox.makeId('foo')});
  138. document.body.appendChild(label);
  139. document.body.appendChild(label2);
  140. try {
  141. checkbox.setChecked(false);
  142. checkbox.setLabel(label);
  143. checkbox.render(label);
  144. assertNotNull(checkbox.getElement());
  145. assertEquals(
  146. label.id, goog.a11y.aria.getState(
  147. checkbox.getElement(), goog.a11y.aria.State.LABELLEDBY));
  148. checkbox.setLabel(label2);
  149. assertEquals(
  150. label2.id, goog.a11y.aria.getState(
  151. checkbox.getElement(), goog.a11y.aria.State.LABELLEDBY));
  152. } finally {
  153. document.body.removeChild(label);
  154. document.body.removeChild(label2);
  155. }
  156. }
  157. function testLabel() {
  158. var label = goog.dom.createElement(goog.dom.TagName.DIV);
  159. document.body.appendChild(label);
  160. try {
  161. checkbox.setChecked(false);
  162. checkbox.setLabel(label);
  163. checkbox.render(label);
  164. // Clicking on label toggles checkbox.
  165. goog.testing.events.fireClickSequence(label);
  166. assertTrue(
  167. 'checkbox toggled if the label is clicked', checkbox.getChecked());
  168. goog.testing.events.fireClickSequence(checkbox.getElement());
  169. assertFalse('checkbox toggled if it is clicked', checkbox.getChecked());
  170. // Test that mouse events on the label have the correct effect on the
  171. // checkbox state when it is enabled.
  172. checkbox.setEnabled(true);
  173. goog.testing.events.fireMouseOverEvent(label);
  174. assertTrue(checkbox.hasState(goog.ui.Component.State.HOVER));
  175. assertContains(
  176. 'checkbox gets hover state on mouse over', 'goog-checkbox-hover',
  177. goog.dom.classlist.get(checkbox.getElement()));
  178. goog.testing.events.fireMouseDownEvent(label);
  179. assertTrue(checkbox.hasState(goog.ui.Component.State.ACTIVE));
  180. assertContains(
  181. 'checkbox gets active state on label mousedown', 'goog-checkbox-active',
  182. goog.dom.classlist.get(checkbox.getElement()));
  183. goog.testing.events.fireMouseOutEvent(checkbox.getElement());
  184. assertFalse(checkbox.hasState(goog.ui.Component.State.HOVER));
  185. assertNotContains(
  186. 'checkbox does not have hover state after mouse out',
  187. 'goog-checkbox-hover', goog.dom.classlist.get(checkbox.getElement()));
  188. assertFalse(checkbox.hasState(goog.ui.Component.State.ACTIVE));
  189. assertNotContains(
  190. 'checkbox does not have active state after mouse out',
  191. 'goog-checkbox-active', goog.dom.classlist.get(checkbox.getElement()));
  192. // Test label mouse events on disabled checkbox.
  193. checkbox.setEnabled(false);
  194. goog.testing.events.fireMouseOverEvent(label);
  195. assertFalse(checkbox.hasState(goog.ui.Component.State.HOVER));
  196. assertNotContains(
  197. 'disabled checkbox does not get hover state on mouseover',
  198. 'goog-checkbox-hover', goog.dom.classlist.get(checkbox.getElement()));
  199. goog.testing.events.fireMouseDownEvent(label);
  200. assertFalse(checkbox.hasState(goog.ui.Component.State.ACTIVE));
  201. assertNotContains(
  202. 'disabled checkbox does not get active state mousedown',
  203. 'goog-checkbox-active', goog.dom.classlist.get(checkbox.getElement()));
  204. goog.testing.events.fireMouseOutEvent(checkbox.getElement());
  205. assertFalse(checkbox.hasState(goog.ui.Component.State.ACTIVE));
  206. assertNotContains(
  207. 'checkbox does not get stuck in hover state', 'goog-checkbox-hover',
  208. goog.dom.classlist.get(checkbox.getElement()));
  209. // Making the label null prevents it from affecting checkbox state.
  210. checkbox.setEnabled(true);
  211. checkbox.setLabel(null);
  212. goog.testing.events.fireClickSequence(label);
  213. assertFalse('label element deactivated', checkbox.getChecked());
  214. goog.testing.events.fireClickSequence(checkbox.getElement());
  215. assertTrue('checkbox still active', checkbox.getChecked());
  216. } finally {
  217. document.body.removeChild(label);
  218. }
  219. }
  220. function testLabel_setAgain() {
  221. var label = goog.dom.createElement(goog.dom.TagName.DIV);
  222. document.body.appendChild(label);
  223. try {
  224. checkbox.setChecked(false);
  225. checkbox.setLabel(label);
  226. checkbox.render(label);
  227. checkbox.getElement().focus();
  228. checkbox.setLabel(label);
  229. assertEquals(
  230. 'checkbox should not have lost focus', checkbox.getElement(),
  231. document.activeElement);
  232. } finally {
  233. document.body.removeChild(label);
  234. }
  235. }
  236. function testConstructor() {
  237. assertEquals(
  238. 'state is unchecked', goog.ui.Checkbox.State.UNCHECKED,
  239. checkbox.getChecked());
  240. var testCheckboxWithState =
  241. new goog.ui.Checkbox(goog.ui.Checkbox.State.UNDETERMINED);
  242. assertNotNull('checkbox created with custom state', testCheckboxWithState);
  243. assertEquals(
  244. 'checkbox state is undetermined', goog.ui.Checkbox.State.UNDETERMINED,
  245. testCheckboxWithState.getChecked());
  246. testCheckboxWithState.dispose();
  247. }
  248. function testCustomRenderer() {
  249. var cssClass = 'my-custom-checkbox';
  250. var renderer = goog.ui.ControlRenderer.getCustomRenderer(
  251. goog.ui.CheckboxRenderer, cssClass);
  252. var customCheckbox = new goog.ui.Checkbox(undefined, undefined, renderer);
  253. customCheckbox.createDom();
  254. assertElementsEquals(
  255. ['my-custom-checkbox', 'my-custom-checkbox-unchecked'],
  256. goog.dom.classlist.get(customCheckbox.getElement()));
  257. customCheckbox.setChecked(true);
  258. assertElementsEquals(
  259. ['my-custom-checkbox', 'my-custom-checkbox-checked'],
  260. goog.dom.classlist.get(customCheckbox.getElement()));
  261. customCheckbox.setChecked(null);
  262. assertElementsEquals(
  263. ['my-custom-checkbox', 'my-custom-checkbox-undetermined'],
  264. goog.dom.classlist.get(customCheckbox.getElement()));
  265. customCheckbox.dispose();
  266. }
  267. function testGetAriaRole() {
  268. checkbox.createDom();
  269. assertNotNull(checkbox.getElement());
  270. assertEquals(
  271. "Checkbox's ARIA role should be 'checkbox'", goog.a11y.aria.Role.CHECKBOX,
  272. goog.a11y.aria.getRole(checkbox.getElement()));
  273. }
  274. function testCreateDomUpdateAriaState() {
  275. checkbox.createDom();
  276. assertNotNull(checkbox.getElement());
  277. assertEquals(
  278. 'Checkbox must have default false ARIA state aria-checked', 'false',
  279. goog.a11y.aria.getState(
  280. checkbox.getElement(), goog.a11y.aria.State.CHECKED));
  281. checkbox.setChecked(goog.ui.Checkbox.State.CHECKED);
  282. assertEquals(
  283. 'Checkbox must have true ARIA state aria-checked', 'true',
  284. goog.a11y.aria.getState(
  285. checkbox.getElement(), goog.a11y.aria.State.CHECKED));
  286. checkbox.setChecked(goog.ui.Checkbox.State.UNCHECKED);
  287. assertEquals(
  288. 'Checkbox must have false ARIA state aria-checked', 'false',
  289. goog.a11y.aria.getState(
  290. checkbox.getElement(), goog.a11y.aria.State.CHECKED));
  291. checkbox.setChecked(goog.ui.Checkbox.State.UNDETERMINED);
  292. assertEquals(
  293. 'Checkbox must have mixed ARIA state aria-checked', 'mixed',
  294. goog.a11y.aria.getState(
  295. checkbox.getElement(), goog.a11y.aria.State.CHECKED));
  296. }
  297. function testDecorateUpdateAriaState() {
  298. var decorateSpan = goog.dom.getElement('decorate');
  299. checkbox.decorate(decorateSpan);
  300. assertEquals(
  301. 'Checkbox must have default false ARIA state aria-checked', 'false',
  302. goog.a11y.aria.getState(
  303. checkbox.getElement(), goog.a11y.aria.State.CHECKED));
  304. checkbox.setChecked(goog.ui.Checkbox.State.CHECKED);
  305. assertEquals(
  306. 'Checkbox must have true ARIA state aria-checked', 'true',
  307. goog.a11y.aria.getState(
  308. checkbox.getElement(), goog.a11y.aria.State.CHECKED));
  309. checkbox.setChecked(goog.ui.Checkbox.State.UNCHECKED);
  310. assertEquals(
  311. 'Checkbox must have false ARIA state aria-checked', 'false',
  312. goog.a11y.aria.getState(
  313. checkbox.getElement(), goog.a11y.aria.State.CHECKED));
  314. checkbox.setChecked(goog.ui.Checkbox.State.UNDETERMINED);
  315. assertEquals(
  316. 'Checkbox must have mixed ARIA state aria-checked', 'mixed',
  317. goog.a11y.aria.getState(
  318. checkbox.getElement(), goog.a11y.aria.State.CHECKED));
  319. }
  320. function testSpaceKey() {
  321. var normalSpan = goog.dom.getElement('normal');
  322. checkbox.decorate(normalSpan);
  323. assertEquals(
  324. 'default state is unchecked', goog.ui.Checkbox.State.UNCHECKED,
  325. checkbox.getChecked());
  326. goog.testing.events.fireKeySequence(normalSpan, goog.events.KeyCodes.SPACE);
  327. assertEquals(
  328. 'SPACE toggles checkbox to be checked', goog.ui.Checkbox.State.CHECKED,
  329. checkbox.getChecked());
  330. goog.testing.events.fireKeySequence(normalSpan, goog.events.KeyCodes.SPACE);
  331. assertEquals(
  332. 'another SPACE toggles checkbox to be unchecked',
  333. goog.ui.Checkbox.State.UNCHECKED, checkbox.getChecked());
  334. // Enter for example doesn't work
  335. goog.testing.events.fireKeySequence(normalSpan, goog.events.KeyCodes.ENTER);
  336. assertEquals(
  337. 'Enter leaves checkbox unchecked', goog.ui.Checkbox.State.UNCHECKED,
  338. checkbox.getChecked());
  339. }
  340. function testSpaceKeyFiresEvents() {
  341. var normalSpan = goog.dom.getElement('normal');
  342. checkbox.decorate(normalSpan);
  343. var events = [];
  344. goog.events.listen(
  345. checkbox,
  346. [
  347. goog.ui.Component.EventType.ACTION, goog.ui.Component.EventType.CHECK,
  348. goog.ui.Component.EventType.UNCHECK, goog.ui.Component.EventType.CHANGE
  349. ],
  350. function(e) { events.push(e.type); });
  351. assertEquals(
  352. 'Unexpected default state.', goog.ui.Checkbox.State.UNCHECKED,
  353. checkbox.getChecked());
  354. goog.testing.events.fireKeySequence(normalSpan, goog.events.KeyCodes.SPACE);
  355. assertArrayEquals(
  356. 'Unexpected events fired when checking with spacebar.',
  357. [
  358. goog.ui.Component.EventType.ACTION, goog.ui.Component.EventType.CHECK,
  359. goog.ui.Component.EventType.CHANGE
  360. ],
  361. events);
  362. assertEquals(
  363. 'Unexpected state after checking.', goog.ui.Checkbox.State.CHECKED,
  364. checkbox.getChecked());
  365. events = [];
  366. goog.testing.events.fireKeySequence(normalSpan, goog.events.KeyCodes.SPACE);
  367. assertArrayEquals(
  368. 'Unexpected events fired when unchecking with spacebar.',
  369. [
  370. goog.ui.Component.EventType.ACTION, goog.ui.Component.EventType.UNCHECK,
  371. goog.ui.Component.EventType.CHANGE
  372. ],
  373. events);
  374. assertEquals(
  375. 'Unexpected state after unchecking.', goog.ui.Checkbox.State.UNCHECKED,
  376. checkbox.getChecked());
  377. events = [];
  378. goog.events.listenOnce(
  379. checkbox, goog.ui.Component.EventType.CHECK,
  380. function(e) { e.preventDefault(); });
  381. goog.testing.events.fireKeySequence(normalSpan, goog.events.KeyCodes.SPACE);
  382. assertArrayEquals(
  383. 'Unexpected events fired when checking with spacebar and ' +
  384. 'the check event is cancelled.',
  385. [goog.ui.Component.EventType.ACTION, goog.ui.Component.EventType.CHECK],
  386. events);
  387. assertEquals(
  388. 'Unexpected state after check event is cancelled.',
  389. goog.ui.Checkbox.State.UNCHECKED, checkbox.getChecked());
  390. }
  391. function testDecorate() {
  392. var normalSpan = goog.dom.getElement('normal');
  393. var checkedSpan = goog.dom.getElement('checked');
  394. var uncheckedSpan = goog.dom.getElement('unchecked');
  395. var undeterminedSpan = goog.dom.getElement('undetermined');
  396. var disabledSpan = goog.dom.getElement('disabled');
  397. validateCheckBox(normalSpan, goog.ui.Checkbox.State.UNCHECKED);
  398. validateCheckBox(checkedSpan, goog.ui.Checkbox.State.CHECKED);
  399. validateCheckBox(uncheckedSpan, goog.ui.Checkbox.State.UNCHECKED);
  400. validateCheckBox(undeterminedSpan, goog.ui.Checkbox.State.UNDETERMINED);
  401. validateCheckBox(disabledSpan, goog.ui.Checkbox.State.UNCHECKED, true);
  402. }
  403. function validateCheckBox(span, state, opt_disabled) {
  404. var testCheckbox = goog.ui.decorate(span);
  405. assertNotNull('checkbox created', testCheckbox);
  406. assertEquals(
  407. 'decorate was successful', goog.ui.Checkbox, testCheckbox.constructor);
  408. assertEquals(
  409. 'checkbox state should be: ' + state, state, testCheckbox.getChecked());
  410. assertEquals(
  411. 'checkbox is ' + (!opt_disabled ? 'enabled' : 'disabled'), !opt_disabled,
  412. testCheckbox.isEnabled());
  413. testCheckbox.dispose();
  414. }
  415. function testSetAriaLabel() {
  416. assertNull(
  417. 'Checkbox must not have aria label by default', checkbox.getAriaLabel());
  418. checkbox.setAriaLabel('Checkbox 1');
  419. checkbox.render();
  420. var el = checkbox.getElementStrict();
  421. assertEquals(
  422. 'Checkbox element must have expected aria-label', 'Checkbox 1',
  423. el.getAttribute('aria-label'));
  424. assertEquals(
  425. 'Checkbox element must have expected aria-role', 'checkbox',
  426. el.getAttribute('role'));
  427. checkbox.setAriaLabel('Checkbox 2');
  428. assertEquals(
  429. 'Checkbox element must have updated aria-label', 'Checkbox 2',
  430. el.getAttribute('aria-label'));
  431. assertEquals(
  432. 'Checkbox element must have expected aria-role', 'checkbox',
  433. el.getAttribute('role'));
  434. }