component_test.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915
  1. // Copyright 2007 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.ComponentTest');
  15. goog.setTestOnly('goog.ui.ComponentTest');
  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.events.EventTarget');
  21. goog.require('goog.testing.PropertyReplacer');
  22. goog.require('goog.testing.jsunit');
  23. goog.require('goog.ui.Component');
  24. var component;
  25. var propertyReplacer = new goog.testing.PropertyReplacer();
  26. var sandbox;
  27. function setUp() {
  28. sandbox = goog.dom.getElement('sandbox');
  29. component = new goog.ui.Component();
  30. }
  31. function tearDown() {
  32. component.dispose();
  33. goog.dom.removeChildren(sandbox);
  34. propertyReplacer.reset();
  35. }
  36. function testConstructor() {
  37. assertTrue(
  38. 'Instance must be non-null and have the expected class',
  39. component instanceof goog.ui.Component);
  40. assertTrue(
  41. 'DOM helper must be non-null and have the expected class',
  42. component.dom_ instanceof goog.dom.DomHelper);
  43. var fakeDom = {};
  44. var otherComponent = new goog.ui.Component(fakeDom);
  45. assertEquals(
  46. 'DOM helper must refer to expected object', fakeDom, otherComponent.dom_);
  47. otherComponent.dispose();
  48. }
  49. function testGetId() {
  50. assertNull('Component ID should be initialized to null', component.id_);
  51. var id = component.getId();
  52. assertNotNull('Component ID should be generated on demand', id);
  53. assertEquals(
  54. 'Subsequent calls to getId() must return same value', id,
  55. component.getId());
  56. }
  57. function testSetId() {
  58. component.setId('myId');
  59. assertEquals(
  60. 'getId() must return explicitly set ID', 'myId', component.getId());
  61. var child = new goog.ui.Component();
  62. var childId = child.getId();
  63. component.addChild(child);
  64. assertEquals(
  65. 'Parent component must find child by ID', child,
  66. component.getChild(childId));
  67. child.setId('someNewId');
  68. assertEquals(
  69. 'Parent component must find child by new ID', child,
  70. component.getChild('someNewId'));
  71. child.dispose();
  72. }
  73. function testGetSetElement() {
  74. assertNull('Element must be null by default', component.getElement());
  75. var element = goog.dom.createElement(goog.dom.TagName.DIV);
  76. component.setElementInternal(element);
  77. assertEquals(
  78. 'getElement() must return expected element', element,
  79. component.getElement());
  80. }
  81. function testGetSetParent() {
  82. assertNull('Parent must be null by default', component.getParent());
  83. var parent = new goog.ui.Component();
  84. component.setParent(parent);
  85. assertEquals(
  86. 'getParent() must return expected component', parent,
  87. component.getParent());
  88. component.setParent(null);
  89. assertNull('Parent must be null', component.getParent());
  90. assertThrows(
  91. 'Setting a component\'s parent to itself must throw error',
  92. function() { component.setParent(component); });
  93. parent.addChild(component);
  94. assertEquals(
  95. 'getParent() must return expected component', parent,
  96. component.getParent());
  97. assertThrows(
  98. 'Changing a child component\'s parent must throw error',
  99. function() { component.setParent(new goog.ui.Component()); });
  100. parent.dispose();
  101. }
  102. function testGetParentEventTarget() {
  103. assertNull(
  104. 'Parent event target must be null by default',
  105. component.getParentEventTarget());
  106. var parent = new goog.ui.Component();
  107. component.setParent(parent);
  108. assertEquals(
  109. 'Parent event target must be the parent component', parent,
  110. component.getParentEventTarget());
  111. assertThrows(
  112. 'Directly setting the parent event target to other than ' +
  113. 'the parent component when the parent component is set must throw ' +
  114. 'error',
  115. function() { component.setParentEventTarget(new goog.ui.Component()); });
  116. parent.dispose();
  117. }
  118. function testSetParentEventTarget() {
  119. var parentEventTarget = new goog.events.EventTarget();
  120. component.setParentEventTarget(parentEventTarget);
  121. assertEquals('Parent component must be null', null, component.getParent());
  122. parentEventTarget.dispose();
  123. }
  124. function testGetDomHelper() {
  125. var domHelper = new goog.dom.DomHelper();
  126. var component = new goog.ui.Component(domHelper);
  127. assertEquals(
  128. 'Component must return the same DomHelper passed', domHelper,
  129. component.getDomHelper());
  130. }
  131. function testIsInDocument() {
  132. assertFalse(
  133. 'Component must not be in the document by default',
  134. component.isInDocument());
  135. component.enterDocument();
  136. assertTrue('Component must be in the document', component.isInDocument());
  137. }
  138. function testCreateDom() {
  139. assertNull('Component must not have DOM by default', component.getElement());
  140. component.createDom();
  141. assertEquals(
  142. 'Component\'s DOM must be an element node', goog.dom.NodeType.ELEMENT,
  143. component.getElement().nodeType);
  144. }
  145. function testRender() {
  146. assertFalse(
  147. 'Component must not be in the document by default',
  148. component.isInDocument());
  149. assertNull('Component must not have DOM by default', component.getElement());
  150. assertFalse(
  151. 'wasDecorated() must be false before component is rendered',
  152. component.wasDecorated());
  153. component.render(sandbox);
  154. assertTrue(
  155. 'Rendered component must be in the document', component.isInDocument());
  156. assertEquals(
  157. 'Component\'s element must be a child of the parent element', sandbox,
  158. component.getElement().parentNode);
  159. assertFalse(
  160. 'wasDecorated() must still be false for rendered component',
  161. component.wasDecorated());
  162. assertThrows('Trying to re-render component must throw error', function() {
  163. component.render();
  164. });
  165. }
  166. function testRender_NoParent() {
  167. component.render();
  168. assertTrue(
  169. 'Rendered component must be in the document', component.isInDocument());
  170. assertEquals(
  171. 'Component\'s element must be a child of the document body',
  172. document.body, component.getElement().parentNode);
  173. }
  174. function testRender_ParentNotInDocument() {
  175. var parent = new goog.ui.Component();
  176. component.setParent(parent);
  177. assertFalse(
  178. 'Parent component must not be in the document', parent.isInDocument());
  179. assertFalse(
  180. 'Child component must not be in the document', component.isInDocument());
  181. assertNull('Child component must not have DOM', component.getElement());
  182. component.render();
  183. assertFalse(
  184. 'Parent component must not be in the document', parent.isInDocument());
  185. assertFalse(
  186. 'Child component must not be in the document', component.isInDocument());
  187. assertNotNull('Child component must have DOM', component.getElement());
  188. parent.dispose();
  189. }
  190. function testRenderBefore() {
  191. var sibling = goog.dom.createElement(goog.dom.TagName.DIV);
  192. sandbox.appendChild(sibling);
  193. component.renderBefore(sibling);
  194. assertTrue(
  195. 'Rendered component must be in the document', component.isInDocument());
  196. assertEquals(
  197. 'Component\'s element must be a child of the parent element', sandbox,
  198. component.getElement().parentNode);
  199. assertEquals(
  200. 'Component\'s element must have expected nextSibling', sibling,
  201. component.getElement().nextSibling);
  202. }
  203. function testRenderChild() {
  204. var parent = new goog.ui.Component();
  205. parent.createDom();
  206. assertFalse('Parent must not be in the document', parent.isInDocument());
  207. assertNotNull('Parent must have a DOM', parent.getElement());
  208. parent.addChild(component);
  209. assertFalse('Child must not be in the document', component.isInDocument());
  210. assertNull('Child must not have a DOM', component.getElement());
  211. component.render(parent.getElement());
  212. assertFalse('Parent must not be in the document', parent.isInDocument());
  213. assertFalse(
  214. 'Child must not be in the document if the parent isn\'t',
  215. component.isInDocument());
  216. assertNotNull('Child must have a DOM', component.getElement());
  217. assertEquals(
  218. 'Child\'s element must be a child of the parent\'s element',
  219. parent.getElement(), component.getElement().parentNode);
  220. parent.render(sandbox);
  221. assertTrue('Parent must be in the document', parent.isInDocument());
  222. assertTrue('Child must be in the document', component.isInDocument());
  223. parent.dispose();
  224. }
  225. function testDecorate() {
  226. sandbox.innerHTML = '<div id="foo">Foo</div>';
  227. var foo = goog.dom.getElement('foo');
  228. assertFalse(
  229. 'wasDecorated() must be false by default', component.wasDecorated());
  230. component.decorate(foo);
  231. assertTrue('Component must be in the document', component.isInDocument());
  232. assertEquals(
  233. 'Component\'s element must be the decorated element', foo,
  234. component.getElement());
  235. assertTrue(
  236. 'wasDecorated() must be true for decorated component',
  237. component.wasDecorated());
  238. assertThrows(
  239. 'Trying to decorate with a control already in the document' +
  240. ' must throw error',
  241. function() { component.decorate(foo); });
  242. }
  243. function testDecorate_AllowDetached_NotInDocument() {
  244. goog.ui.Component.ALLOW_DETACHED_DECORATION = true;
  245. var element = goog.dom.createElement(goog.dom.TagName.DIV);
  246. component.decorate(element);
  247. assertFalse(
  248. 'Component should not call enterDocument when decorated ' +
  249. 'with an element that is not in the document.',
  250. component.isInDocument());
  251. goog.ui.Component.ALLOW_DETACHED_DECORATION = false;
  252. }
  253. function testDecorate_AllowDetached_InDocument() {
  254. goog.ui.Component.ALLOW_DETACHED_DECORATION = true;
  255. var element = goog.dom.createElement(goog.dom.TagName.DIV);
  256. sandbox.appendChild(element);
  257. component.decorate(element);
  258. assertTrue(
  259. 'Component should call enterDocument when decorated ' +
  260. 'with an element that is in the document.',
  261. component.isInDocument());
  262. goog.ui.Component.ALLOW_DETACHED_DECORATION = false;
  263. }
  264. function testCannotDecorate() {
  265. sandbox.innerHTML = '<div id="foo">Foo</div>';
  266. var foo = goog.dom.getElement('foo');
  267. // Have canDecorate() return false.
  268. propertyReplacer.set(component, 'canDecorate', function() { return false; });
  269. assertThrows(
  270. 'Trying to decorate an element for which canDecorate()' +
  271. ' returns false must throw error',
  272. function() { component.decorate(foo); });
  273. }
  274. function testCanDecorate() {
  275. assertTrue(
  276. 'canDecorate() must return true by default',
  277. component.canDecorate(sandbox));
  278. }
  279. function testWasDecorated() {
  280. assertFalse(
  281. 'wasDecorated() must return false by default', component.wasDecorated());
  282. }
  283. function testDecorateInternal() {
  284. assertNull('Element must be null by default', component.getElement());
  285. var element = goog.dom.createElement(goog.dom.TagName.DIV);
  286. component.decorateInternal(element);
  287. assertEquals(
  288. 'Element must have expected value', element, component.getElement());
  289. }
  290. function testGetElementAndGetElementsByClass() {
  291. sandbox.innerHTML = '<ul id="task-list">' +
  292. '<li class="task">Unclog drain' +
  293. '</ul>' +
  294. '<ul id="completed-tasks">' +
  295. '<li id="groceries" class="task">Buy groceries' +
  296. '<li class="task">Rotate tires' +
  297. '<li class="task">Clean kitchen' +
  298. '</ul>' +
  299. assertNull('Should be nothing to return before the component has a DOM',
  300. component.getElementByClass('task'));
  301. assertEquals(
  302. 'Should return an empty list before the component has a DOM', 0,
  303. component.getElementsByClass('task').length);
  304. component.decorate(goog.dom.getElement('completed-tasks'));
  305. assertEquals(
  306. 'getElementByClass() should return the first completed task', 'groceries',
  307. component.getElementByClass('task').id);
  308. assertEquals(
  309. 'getElementsByClass() should return only the completed tasks', 3,
  310. component.getElementsByClass('task').length);
  311. }
  312. function testGetRequiredElementByClass() {
  313. sandbox.innerHTML = '<ul id="task-list">' +
  314. '<li class="task">Unclog drain' +
  315. '</ul>' +
  316. '<ul id="completed-tasks">' +
  317. '<li id="groceries" class="task">Buy groceries' +
  318. '<li class="task">Rotate tires' +
  319. '<li class="task">Clean kitchen' +
  320. '</ul>';
  321. component.decorate(goog.dom.getElement('completed-tasks'));
  322. assertEquals(
  323. 'getRequiredElementByClass() should return the first completed task',
  324. 'groceries', component.getRequiredElementByClass('task').id);
  325. assertThrows(
  326. 'Attempting to retrieve a required element that does not' +
  327. 'exist should fail',
  328. function() { component.getRequiredElementByClass('undefinedClass'); });
  329. }
  330. function testEnterExitDocument() {
  331. var c1 = new goog.ui.Component();
  332. var c2 = new goog.ui.Component();
  333. component.addChild(c1);
  334. component.addChild(c2);
  335. component.createDom();
  336. c1.createDom();
  337. c2.createDom();
  338. assertFalse('Parent must not be in the document', component.isInDocument());
  339. assertFalse(
  340. 'Neither child must be in the document',
  341. c1.isInDocument() || c2.isInDocument());
  342. component.enterDocument();
  343. assertTrue('Parent must be in the document', component.isInDocument());
  344. assertTrue(
  345. 'Both children must be in the document',
  346. c1.isInDocument() && c2.isInDocument());
  347. component.exitDocument();
  348. assertFalse('Parent must not be in the document', component.isInDocument());
  349. assertFalse(
  350. 'Neither child must be in the document',
  351. c1.isInDocument() || c2.isInDocument());
  352. c1.dispose();
  353. c2.dispose();
  354. }
  355. function testDispose() {
  356. var c1, c2;
  357. component.createDom();
  358. component.addChild((c1 = new goog.ui.Component()), true);
  359. component.addChild((c2 = new goog.ui.Component()), true);
  360. var element = component.getElement();
  361. var c1Element = c1.getElement();
  362. var c2Element = c2.getElement();
  363. component.render(sandbox);
  364. assertTrue('Parent must be in the document', component.isInDocument());
  365. assertEquals(
  366. 'Parent\'s element must be a child of the sandbox element', sandbox,
  367. element.parentNode);
  368. assertTrue(
  369. 'Both children must be in the document',
  370. c1.isInDocument() && c2.isInDocument());
  371. assertEquals(
  372. 'First child\'s element must be a child of the parent\'s' +
  373. ' element',
  374. element, c1Element.parentNode);
  375. assertEquals(
  376. 'Second child\'s element must be a child of the parent\'s' +
  377. ' element',
  378. element, c2Element.parentNode);
  379. assertFalse('Parent must not have been disposed of', component.isDisposed());
  380. assertFalse(
  381. 'Neither child must have been disposed of',
  382. c1.isDisposed() || c2.isDisposed());
  383. component.dispose();
  384. assertTrue('Parent must have been disposed of', component.isDisposed());
  385. assertFalse('Parent must not be in the document', component.isInDocument());
  386. assertNotEquals(
  387. 'Parent\'s element must no longer be a child of the' +
  388. ' sandbox element',
  389. sandbox, element.parentNode);
  390. assertTrue(
  391. 'Both children must have been disposed of',
  392. c1.isDisposed() && c2.isDisposed());
  393. assertFalse(
  394. 'Neither child must be in the document',
  395. c1.isInDocument() || c2.isInDocument());
  396. assertNotEquals(
  397. 'First child\'s element must no longer be a child of' +
  398. ' the parent\'s element',
  399. element, c1Element.parentNode);
  400. assertNotEquals(
  401. 'Second child\'s element must no longer be a child of' +
  402. ' the parent\'s element',
  403. element, c2Element.parentNode);
  404. }
  405. function testDispose_Decorated() {
  406. sandbox.innerHTML = '<div id="foo">Foo</div>';
  407. var foo = goog.dom.getElement('foo');
  408. component.decorate(foo);
  409. assertTrue('Component must be in the document', component.isInDocument());
  410. assertFalse(
  411. 'Component must not have been disposed of', component.isDisposed());
  412. assertEquals(
  413. 'Component\'s element must have expected value', foo,
  414. component.getElement());
  415. assertEquals(
  416. 'Decorated element must be a child of the sandbox', sandbox,
  417. foo.parentNode);
  418. component.dispose();
  419. assertFalse(
  420. 'Component must not be in the document', component.isInDocument());
  421. assertTrue('Component must have been disposed of', component.isDisposed());
  422. assertNull('Component\'s element must be null', component.getElement());
  423. assertEquals(
  424. 'Previously decorated element must still be a child of the' +
  425. ' sandbox',
  426. sandbox, foo.parentNode);
  427. }
  428. function testMakeIdAndGetFragmentFromId() {
  429. assertEquals(
  430. 'Unique id must have expected value', component.getId() + '.foo',
  431. component.makeId('foo'));
  432. assertEquals(
  433. 'Fragment must have expected value', 'foo',
  434. component.getFragmentFromId(component.makeId('foo')));
  435. }
  436. function testMakeIdsWithObject() {
  437. var EnumDef = {ENUM_1: 'enum 1', ENUM_2: 'enum 2', ENUM_3: 'enum 3'};
  438. var ids = component.makeIds(EnumDef);
  439. assertEquals(component.makeId(EnumDef.ENUM_1), ids.ENUM_1);
  440. assertEquals(component.makeId(EnumDef.ENUM_2), ids.ENUM_2);
  441. assertEquals(component.makeId(EnumDef.ENUM_3), ids.ENUM_3);
  442. }
  443. function testGetElementByFragment() {
  444. component.render(sandbox);
  445. var element = component.dom_.createDom(
  446. goog.dom.TagName.DIV, {id: component.makeId('foo')}, 'Hello');
  447. sandbox.appendChild(element);
  448. assertEquals(
  449. 'Element must have expected value', element,
  450. component.getElementByFragment('foo'));
  451. }
  452. function testGetSetModel() {
  453. assertNull('Model must be null by default', component.getModel());
  454. var model = 'someModel';
  455. component.setModel(model);
  456. assertEquals('Model must have expected value', model, component.getModel());
  457. component.setModel(null);
  458. assertNull('Model must be null', component.getModel());
  459. }
  460. function testAddChild() {
  461. var child = new goog.ui.Component();
  462. child.setId('child');
  463. assertFalse('Parent must not be in the document', component.isInDocument());
  464. component.addChild(child);
  465. assertTrue('Parent must have children.', component.hasChildren());
  466. assertEquals('Child must have expected parent', component, child.getParent());
  467. assertEquals(
  468. 'Parent must find child by ID', child, component.getChild('child'));
  469. }
  470. function testAddChild_Render() {
  471. var child = new goog.ui.Component();
  472. component.render(sandbox);
  473. assertTrue('Parent must be in the document', component.isInDocument());
  474. assertEquals(
  475. 'Parent must be in the sandbox', sandbox,
  476. component.getElement().parentNode);
  477. component.addChild(child, true);
  478. assertTrue('Child must be in the document', child.isInDocument());
  479. assertEquals(
  480. 'Child element must be a child of the parent element',
  481. component.getElement(), child.getElement().parentNode);
  482. }
  483. function testAddChild_DomOnly() {
  484. var child = new goog.ui.Component();
  485. component.createDom();
  486. assertNotNull('Parent must have a DOM', component.getElement());
  487. assertFalse('Parent must not be in the document', component.isInDocument());
  488. component.addChild(child, true);
  489. assertNotNull('Child must have a DOM', child.getElement());
  490. assertEquals(
  491. 'Child element must be a child of the parent element',
  492. component.getElement(), child.getElement().parentNode);
  493. assertFalse('Child must not be in the document', child.isInDocument());
  494. }
  495. function testAddChildAt() {
  496. var a = new goog.ui.Component();
  497. var b = new goog.ui.Component();
  498. var c = new goog.ui.Component();
  499. var d = new goog.ui.Component();
  500. a.setId('a');
  501. b.setId('b');
  502. c.setId('c');
  503. d.setId('d');
  504. component.addChildAt(b, 0);
  505. assertEquals('b', component.getChildIds().join(''));
  506. component.addChildAt(d, 1);
  507. assertEquals('bd', component.getChildIds().join(''));
  508. component.addChildAt(a, 0);
  509. assertEquals('abd', component.getChildIds().join(''));
  510. component.addChildAt(c, 2);
  511. assertEquals('abcd', component.getChildIds().join(''));
  512. assertEquals(a, component.getChildAt(0));
  513. assertEquals(b, component.getChildAt(1));
  514. assertEquals(c, component.getChildAt(2));
  515. assertEquals(d, component.getChildAt(3));
  516. assertThrows(
  517. 'Adding child at out-of-bounds index must throw error',
  518. function() { component.addChildAt(new goog.ui.Component(), 5); });
  519. }
  520. function testAddChildAtThrowsIfNull() {
  521. assertThrows('Adding a null child must throw an error', function() {
  522. component.addChildAt(null, 0);
  523. });
  524. }
  525. function testHasChildren() {
  526. assertFalse('Component must not have children', component.hasChildren());
  527. component.addChildAt(new goog.ui.Component(), 0);
  528. assertTrue('Component must have children', component.hasChildren());
  529. component.removeChildAt(0);
  530. assertFalse('Component must not have children', component.hasChildren());
  531. }
  532. function testGetChildCount() {
  533. assertEquals('Component must have 0 children', 0, component.getChildCount());
  534. component.addChild(new goog.ui.Component());
  535. assertEquals('Component must have 1 child', 1, component.getChildCount());
  536. component.addChild(new goog.ui.Component());
  537. assertEquals('Component must have 2 children', 2, component.getChildCount());
  538. component.removeChildAt(1);
  539. assertEquals('Component must have 1 child', 1, component.getChildCount());
  540. component.removeChildAt(0);
  541. assertEquals('Component must have 0 children', 0, component.getChildCount());
  542. }
  543. function testGetChildIds() {
  544. var a = new goog.ui.Component();
  545. var b = new goog.ui.Component();
  546. a.setId('a');
  547. b.setId('b');
  548. component.addChild(a);
  549. assertEquals('a', component.getChildIds().join(''));
  550. component.addChild(b);
  551. assertEquals('ab', component.getChildIds().join(''));
  552. var ids = component.getChildIds();
  553. ids.push('c');
  554. assertEquals(
  555. 'Changes to the array returned by getChildIds() must not' +
  556. ' affect the component',
  557. 'ab', component.getChildIds().join(''));
  558. }
  559. function testGetChild() {
  560. assertNull('Parent must have no children', component.getChild('myId'));
  561. var c = new goog.ui.Component();
  562. c.setId('myId');
  563. component.addChild(c);
  564. assertEquals('Parent must find child by ID', c, component.getChild('myId'));
  565. c.setId('newId');
  566. assertNull(
  567. 'Parent must not find child by old ID', component.getChild('myId'));
  568. assertEquals(
  569. 'Parent must find child by new ID', c, component.getChild('newId'));
  570. }
  571. function testGetChildAt() {
  572. var a = new goog.ui.Component();
  573. var b = new goog.ui.Component();
  574. a.setId('a');
  575. b.setId('b');
  576. component.addChildAt(a, 0);
  577. assertEquals('Parent must find child by index', a, component.getChildAt(0));
  578. component.addChildAt(b, 1);
  579. assertEquals('Parent must find child by index', b, component.getChildAt(1));
  580. assertNull(
  581. 'Parent must return null for out-of-bounds index',
  582. component.getChildAt(3));
  583. }
  584. function testForEachChild() {
  585. var invoked = false;
  586. component.forEachChild(function(child) {
  587. assertNotNull('Child must never be null', child);
  588. invoked = true;
  589. });
  590. assertFalse(
  591. 'forEachChild must not call its argument if the parent has ' +
  592. 'no children',
  593. invoked);
  594. component.addChild(new goog.ui.Component());
  595. component.addChild(new goog.ui.Component());
  596. component.addChild(new goog.ui.Component());
  597. var callCount = 0;
  598. component.forEachChild(function(child, index) {
  599. assertEquals(component, this);
  600. callCount++;
  601. }, component);
  602. assertEquals(3, callCount);
  603. }
  604. function testIndexOfChild() {
  605. var a = new goog.ui.Component();
  606. var b = new goog.ui.Component();
  607. var c = new goog.ui.Component();
  608. a.setId('a');
  609. b.setId('b');
  610. c.setId('c');
  611. component.addChild(a);
  612. assertEquals(0, component.indexOfChild(a));
  613. component.addChild(b);
  614. assertEquals(1, component.indexOfChild(b));
  615. component.addChild(c);
  616. assertEquals(2, component.indexOfChild(c));
  617. assertEquals(
  618. 'indexOfChild must return -1 for nonexistent child', -1,
  619. component.indexOfChild(new goog.ui.Component()));
  620. }
  621. function testRemoveChild() {
  622. var a = new goog.ui.Component();
  623. var b = new goog.ui.Component();
  624. var c = new goog.ui.Component();
  625. a.setId('a');
  626. b.setId('b');
  627. c.setId('c');
  628. component.addChild(a);
  629. component.addChild(b);
  630. component.addChild(c);
  631. assertEquals(
  632. 'Parent must remove and return child', c, component.removeChild(c));
  633. assertNull(
  634. 'Parent must no longer contain this child', component.getChild('c'));
  635. assertEquals(
  636. 'Parent must remove and return child by ID', b,
  637. component.removeChild('b'));
  638. assertNull(
  639. 'Parent must no longer contain this child', component.getChild('b'));
  640. assertEquals(
  641. 'Parent must remove and return child by index', a,
  642. component.removeChildAt(0));
  643. assertNull(
  644. 'Parent must no longer contain this child', component.getChild('a'));
  645. }
  646. function testMovingChildrenUsingAddChildAt() {
  647. component.render(sandbox);
  648. var a = new goog.ui.Component();
  649. var b = new goog.ui.Component();
  650. var c = new goog.ui.Component();
  651. var d = new goog.ui.Component();
  652. a.setElementInternal(goog.dom.createElement(goog.dom.TagName.A));
  653. b.setElementInternal(goog.dom.createElement(goog.dom.TagName.B));
  654. c.setElementInternal(goog.dom.createElement(goog.dom.TagName.C));
  655. d.setElementInternal(goog.dom.createElement(goog.dom.TagName.D));
  656. a.setId('a');
  657. b.setId('b');
  658. c.setId('c');
  659. d.setId('d');
  660. component.addChild(a, true);
  661. component.addChild(b, true);
  662. component.addChild(c, true);
  663. component.addChild(d, true);
  664. assertEquals('abcd', component.getChildIds().join(''));
  665. assertEquals(a, component.getChildAt(0));
  666. assertEquals(b, component.getChildAt(1));
  667. assertEquals(c, component.getChildAt(2));
  668. assertEquals(d, component.getChildAt(3));
  669. // Move child d to the top and b to the bottom.
  670. component.addChildAt(d, 0);
  671. component.addChildAt(b, 3);
  672. assertEquals('dacb', component.getChildIds().join(''));
  673. assertEquals(d, component.getChildAt(0));
  674. assertEquals(a, component.getChildAt(1));
  675. assertEquals(c, component.getChildAt(2));
  676. assertEquals(b, component.getChildAt(3));
  677. // Move child a to the top, and check that DOM nodes are in correct order.
  678. component.addChildAt(a, 0);
  679. assertEquals('adcb', component.getChildIds().join(''));
  680. assertEquals(a, component.getChildAt(0));
  681. assertEquals(a.getElement(), component.getElement().childNodes[0]);
  682. }
  683. function testAddChildAfterDomCreatedDoesNotEnterDocument() {
  684. var parent = new goog.ui.Component();
  685. var child = new goog.ui.Component();
  686. var nestedDiv = goog.dom.createDom(goog.dom.TagName.DIV);
  687. parent.setElementInternal(
  688. goog.dom.createDom(goog.dom.TagName.DIV, undefined, nestedDiv));
  689. parent.render();
  690. // Now add a child, whose DOM already exists. This happens, for example,
  691. // if the child itself performs an addChild(x, true).
  692. child.createDom();
  693. parent.addChild(child, false);
  694. // The parent shouldn't call enterDocument on the child, since the child
  695. // actually isn't in the document yet.
  696. assertFalse(child.isInDocument());
  697. // Now, actually render the child; it should be in the document.
  698. child.render(nestedDiv);
  699. assertTrue(child.isInDocument());
  700. assertEquals(
  701. 'Child should be rendered in the expected div', nestedDiv,
  702. child.getElement().parentNode);
  703. }
  704. function testAddChildAfterDomManuallyInserted() {
  705. var parent = new goog.ui.Component();
  706. var child = new goog.ui.Component();
  707. var nestedDiv = goog.dom.createDom(goog.dom.TagName.DIV);
  708. parent.setElementInternal(
  709. goog.dom.createDom(goog.dom.TagName.DIV, undefined, nestedDiv));
  710. parent.render();
  711. // This sequence is weird, but some people do it instead of just manually
  712. // doing render. The addChild will detect that the child is in the DOM
  713. // and call enterDocument.
  714. child.createDom();
  715. nestedDiv.appendChild(child.getElement());
  716. parent.addChild(child, false);
  717. assertTrue(child.isInDocument());
  718. assertEquals(
  719. 'Child should be rendered in the expected div', nestedDiv,
  720. child.getElement().parentNode);
  721. }
  722. function testRemoveChildren() {
  723. var a = new goog.ui.Component();
  724. var b = new goog.ui.Component();
  725. var c = new goog.ui.Component();
  726. component.addChild(a);
  727. component.addChild(b);
  728. component.addChild(c);
  729. a.setId('a');
  730. b.setId('b');
  731. c.setId('c');
  732. assertArrayEquals(
  733. 'Parent must remove and return children.', [a, b, c],
  734. component.removeChildren());
  735. assertNull(
  736. 'Parent must no longer contain this child', component.getChild('a'));
  737. assertNull(
  738. 'Parent must no longer contain this child', component.getChild('b'));
  739. assertNull(
  740. 'Parent must no longer contain this child', component.getChild('c'));
  741. }
  742. function testRemoveChildren_Unrender() {
  743. var a = new goog.ui.Component();
  744. var b = new goog.ui.Component();
  745. component.render(sandbox);
  746. component.addChild(a);
  747. component.addChild(b);
  748. assertArrayEquals(
  749. 'Prent must remove and return children.', [a, b],
  750. component.removeChildren(true));
  751. assertNull(
  752. 'Parent must no longer contain this child', component.getChild('a'));
  753. assertFalse('Child must no longer be in the document.', a.isInDocument());
  754. assertNull(
  755. 'Parent must no longer contain this child', component.getChild('b'));
  756. assertFalse('Child must no longer be in the document.', b.isInDocument());
  757. }