// Copyright 2008 The Closure Library Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. goog.provide('goog.ui.MenuButtonTest'); goog.setTestOnly('goog.ui.MenuButtonTest'); goog.require('goog.Timer'); goog.require('goog.a11y.aria'); goog.require('goog.a11y.aria.State'); goog.require('goog.dom'); goog.require('goog.dom.TagName'); goog.require('goog.events'); goog.require('goog.events.Event'); goog.require('goog.events.EventType'); goog.require('goog.events.KeyCodes'); goog.require('goog.events.KeyHandler'); goog.require('goog.positioning'); goog.require('goog.positioning.Corner'); goog.require('goog.positioning.MenuAnchoredPosition'); goog.require('goog.positioning.Overflow'); goog.require('goog.style'); goog.require('goog.testing.ExpectedFailures'); goog.require('goog.testing.PropertyReplacer'); goog.require('goog.testing.events'); goog.require('goog.testing.jsunit'); goog.require('goog.testing.recordFunction'); goog.require('goog.ui.Component'); goog.require('goog.ui.Menu'); goog.require('goog.ui.MenuButton'); goog.require('goog.ui.MenuItem'); goog.require('goog.ui.SubMenu'); goog.require('goog.userAgent'); goog.require('goog.userAgent.product'); goog.require('goog.userAgent.product.isVersion'); var menuButton; var clonedMenuButtonDom; var expectedFailures; function setUpPage() { expectedFailures = new goog.testing.ExpectedFailures(); } // Mock out goog.positioning.positionAtCoordinate to always ignore failure when // the window is too small, since we don't care about the viewport size on // the selenium farm. // TODO(nicksantos): Move this into a common location if we ever have enough // code for a general goog.testing.ui library. var originalPositionAtCoordinate = goog.positioning.positionAtCoordinate; goog.positioning.positionAtCoordinate = function( absolutePos, movableElement, movableElementCorner, opt_margin, opt_viewport, opt_overflow, opt_preferredSize) { return originalPositionAtCoordinate.call( this, absolutePos, movableElement, movableElementCorner, opt_margin, opt_viewport, goog.positioning.Overflow.IGNORE, opt_preferredSize); }; function MyFakeEvent(keyCode, opt_eventType) { this.type = opt_eventType || goog.events.KeyHandler.EventType.KEY; this.keyCode = keyCode; this.propagationStopped = false; this.preventDefault = goog.nullFunction; this.stopPropagation = function() { this.propagationStopped = true; }; } function setUp() { window.scrollTo(0, 0); var viewportSize = goog.dom.getViewportSize(); // Some tests need enough size viewport. if (viewportSize.width < 600 || viewportSize.height < 600) { window.moveTo(0, 0); window.resizeTo(640, 640); } clonedMenuButtonDom = goog.dom.getElement('demoMenuButton').cloneNode(true); menuButton = new goog.ui.MenuButton(); } function tearDown() { expectedFailures.handleTearDown(); menuButton.dispose(); var element = goog.dom.getElement('demoMenuButton'); element.parentNode.replaceChild(clonedMenuButtonDom, element); } /** * Check if the aria-haspopup property is set correctly. */ function checkHasPopUp() { menuButton.enterDocument(); assertFalse( 'Menu button must have aria-haspopup attribute set to false', goog.a11y.aria.getState( menuButton.getElement(), goog.a11y.aria.State.HASPOPUP)); var menu = new goog.ui.Menu(); menu.createDom(); menuButton.setMenu(menu); assertTrue( 'Menu button must have aria-haspopup attribute set to true', goog.a11y.aria.getState( menuButton.getElement(), goog.a11y.aria.State.HASPOPUP)); menuButton.setMenu(null); assertFale( 'Menu button must have aria-haspopup attribute set to false', goog.a11y.aria.getState( menuButton.getElement(), goog.a11y.aria.State.HASPOPUP)); } /** * Open the menu and click on the menu item inside. * Check if the aria-haspopup property is set correctly. */ function testBasicButtonBehavior() { var node = goog.dom.getElement('demoMenuButton'); menuButton.decorate(node); assertEquals( 'Menu button must have aria-haspopup attribute set to true', 'true', goog.a11y.aria.getState( menuButton.getElement(), goog.a11y.aria.State.HASPOPUP)); goog.testing.events.fireClickSequence(node); assertTrue('Menu must open after click', menuButton.isOpen()); var menuItemClicked = 0; var lastMenuItemClicked = null; goog.events.listen( menuButton.getMenu(), goog.ui.Component.EventType.ACTION, function(e) { menuItemClicked++; lastMenuItemClicked = e.target; }); var menuItem2 = goog.dom.getElement('menuItem2'); goog.testing.events.fireClickSequence(menuItem2); assertFalse('Menu must close on clicking when open', menuButton.isOpen()); assertEquals('Number of menu items clicked should be 1', 1, menuItemClicked); assertEquals( 'menuItem2 should be the last menuitem clicked', menuItem2, lastMenuItemClicked.getElement()); } /** * Open the menu, highlight first menuitem and then the second. * Check if the aria-activedescendant property is set correctly. */ function testHighlightItemBehavior() { var node = goog.dom.getElement('demoMenuButton'); menuButton.decorate(node); goog.testing.events.fireClickSequence(node); assertTrue('Menu must open after click', menuButton.isOpen()); menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.DOWN)); assertNotNull(menuButton.getElement()); assertEquals( 'First menuitem must be the aria-activedescendant', 'menuItem1', goog.a11y.aria.getState( menuButton.getElement(), goog.a11y.aria.State.ACTIVEDESCENDANT)); menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.DOWN)); assertEquals( 'Second menuitem must be the aria-activedescendant', 'menuItem2', goog.a11y.aria.getState( menuButton.getElement(), goog.a11y.aria.State.ACTIVEDESCENDANT)); } /** * Check that the appropriate items are selected when menus are opened with the * keyboard and setSelectFirstOnEnterOrSpace is not set. */ function testHighlightFirstOnOpen() { var node = goog.dom.getElement('demoMenuButton'); menuButton.decorate(node); menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.ENTER)); assertEquals( 'By default no items should be highlighted when opened with enter.', null, menuButton.getMenu().getHighlighted()); menuButton.setOpen(false); menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.DOWN)); assertTrue('Menu must open after down key', menuButton.isOpen()); assertEquals( 'First menuitem must be highlighted', 'menuItem1', menuButton.getMenu().getHighlighted().getElement().id); } /** * Check that the appropriate items are selected when menus are opened with the * keyboard, setSelectFirstOnEnterOrSpace is not set, and the first menu item is * disabled. */ function testHighlightFirstOnOpen_withFirstDisabled() { var node = goog.dom.getElement('demoMenuButton'); menuButton.decorate(node); var menu = menuButton.getMenu(); menu.getItemAt(0).setEnabled(false); menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.ENTER)); assertEquals( 'By default no items should be highlighted when opened with enter.', null, menuButton.getMenu().getHighlighted()); menuButton.setOpen(false); menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.DOWN)); assertTrue('Menu must open after down key', menuButton.isOpen()); assertEquals( 'First enabled menuitem must be highlighted', 'menuItem2', menuButton.getMenu().getHighlighted().getElement().id); } /** * Check that the appropriate items are selected when menus are opened with the * keyboard and setSelectFirstOnEnterOrSpace is set. */ function testHighlightFirstOnOpen_withEnterOrSpaceSet() { var node = goog.dom.getElement('demoMenuButton'); menuButton.decorate(node); menuButton.setSelectFirstOnEnterOrSpace(true); menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.ENTER)); assertEquals( 'The first item should be highlighted when opened with enter ' + 'after setting selectFirstOnEnterOrSpace', 'menuItem1', menuButton.getMenu().getHighlighted().getElement().id); } /** * Check that the appropriate item is selected when a menu is opened with the * keyboard, setSelectFirstOnEnterOrSpace is true, and the first menu item is * disabled. */ function testHighlightFirstOnOpen_withEnterOrSpaceSetAndFirstDisabled() { var node = goog.dom.getElement('demoMenuButton'); menuButton.decorate(node); menuButton.setSelectFirstOnEnterOrSpace(true); var menu = menuButton.getMenu(); menu.getItemAt(0).setEnabled(false); menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.ENTER)); assertEquals( 'The first enabled item should be highlighted when opened ' + 'with enter after setting selectFirstOnEnterOrSpace', 'menuItem2', menuButton.getMenu().getHighlighted().getElement().id); } /** * Open the menu, enter a submenu and then back out of it. * Check if the aria-activedescendant property is set correctly. */ function testCloseSubMenuBehavior() { var node = goog.dom.getElement('demoMenuButton'); menuButton.decorate(node); var menu = menuButton.getMenu(); var subMenu = new goog.ui.SubMenu('Submenu'); menu.addItem(subMenu); subMenu.getElement().id = 'subMenu'; var subMenuMenu = new goog.ui.Menu(); subMenu.setMenu(subMenuMenu); var subMenuItem = new goog.ui.MenuItem('Submenu item 1'); subMenuMenu.addItem(subMenuItem); subMenuItem.getElement().id = 'subMenuItem1'; menuButton.setOpen(true); for (var i = 0; i < 4; i++) { menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.DOWN)); } assertEquals( 'Submenu must be the aria-activedescendant', 'subMenu', goog.a11y.aria.getState( menuButton.getElement(), goog.a11y.aria.State.ACTIVEDESCENDANT)); menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.RIGHT)); assertEquals( 'Submenu item 1 must be the aria-activedescendant', 'subMenuItem1', goog.a11y.aria.getState( menuButton.getElement(), goog.a11y.aria.State.ACTIVEDESCENDANT)); menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.LEFT)); assertEquals( 'Submenu must be the aria-activedescendant', 'subMenu', goog.a11y.aria.getState( menuButton.getElement(), goog.a11y.aria.State.ACTIVEDESCENDANT)); } /** * Make sure the menu opens when enter is pressed. */ function testEnterOpensMenu() { var node = goog.dom.getElement('demoMenuButton'); menuButton.decorate(node); menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.ENTER)); assertTrue('Menu must open after enter', menuButton.isOpen()); } /** * Tests the behavior of the enter and space keys when the menu is open. */ function testSpaceOrEnterClosesMenu() { var node = goog.dom.getElement('demoMenuButton'); menuButton.decorate(node); menuButton.setOpen(true); menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.ENTER)); assertFalse('Menu should close after pressing Enter', menuButton.isOpen()); menuButton.setOpen(true); menuButton.handleKeyEvent( new MyFakeEvent(goog.events.KeyCodes.SPACE, goog.events.EventType.KEYUP)); assertFalse('Menu should close after pressing Space', menuButton.isOpen()); } /** * Tests that a keydown event of the escape key propagates normally when the * menu is closed. */ function testStopEscapePropagationMenuClosed() { var node = goog.dom.getElement('demoMenuButton'); var fakeEvent = new MyFakeEvent( goog.events.KeyCodes.ESCAPE, goog.events.EventType.KEYDOWN); menuButton.decorate(node); menuButton.setOpen(false); menuButton.handleKeyDownEvent_(fakeEvent); assertFalse( 'Event propagation was erroneously stopped.', fakeEvent.propagationStopped); } /** * Tests that a keydown event of the escape key is prevented from propagating * when the menu is open. */ function testStopEscapePropagationMenuOpen() { var node = goog.dom.getElement('demoMenuButton'); var fakeEvent = new MyFakeEvent( goog.events.KeyCodes.ESCAPE, goog.events.EventType.KEYDOWN); menuButton.decorate(node); menuButton.setOpen(true); menuButton.handleKeyDownEvent_(fakeEvent); assertTrue( 'Event propagation was not stopped.', fakeEvent.propagationStopped); } /** * Open the menu and click on the menu item inside after exiting and entering * the document once, to test proper setup/teardown behavior of MenuButton. */ function testButtonAfterEnterDocument() { var node = goog.dom.getElement('demoMenuButton'); menuButton.decorate(node); menuButton.exitDocument(); menuButton.enterDocument(); goog.testing.events.fireClickSequence(node); assertTrue('Menu must open after click', menuButton.isOpen()); var menuItem2 = goog.dom.getElement('menuItem2'); goog.testing.events.fireClickSequence(menuItem2); assertFalse('Menu must close on clicking when open', menuButton.isOpen()); } /** * Renders the menu button, moves its menu and then repositions to make sure the * position is more or less ok. */ function testPositionMenu() { var node = goog.dom.getElement('demoMenuButton'); menuButton.decorate(node); var menu = menuButton.getMenu(); menu.setVisible(true, true); // Move to 500, 500 menu.setPosition(500, 500); // Now reposition and make sure position is more or less ok. menuButton.positionMenu(); var menuNode = goog.dom.getElement('demoMenu'); assertRoughlyEquals( menuNode.offsetTop, node.offsetTop + node.offsetHeight, 20); assertRoughlyEquals(menuNode.offsetLeft, node.offsetLeft, 20); } /** * Tests that calling positionMenu when the menu is not in the document does not * throw an exception. */ function testPositionMenuNotInDocument() { var menu = new goog.ui.Menu(); menu.createDom(); menuButton.setMenu(menu); menuButton.positionMenu(); } /** * Shows the menu and moves the menu button, a timer correct the menu position. */ function testOpenedMenuPositionCorrection() { var iframe = goog.dom.getElement('iframe1'); var iframeDoc = goog.dom.getFrameContentDocument(iframe); var iframeDom = goog.dom.getDomHelper(iframeDoc); var iframeWindow = goog.dom.getWindow(iframeDoc); var button = new goog.ui.MenuButton(); iframeWindow.scrollTo(0, 0); var node = iframeDom.getElement('demoMenuButton'); button.decorate(node); var mockTimer = new goog.Timer(); // Don't start the timer. We manually dispatch the Tick event. mockTimer.start = goog.nullFunction; button.timer_ = mockTimer; var replacer = new goog.testing.PropertyReplacer(); var positionMenuCalled; var origPositionMenu = goog.bind(button.positionMenu, button); replacer.set(button, 'positionMenu', function() { positionMenuCalled = true; origPositionMenu(); }); // Show the menu. button.setOpen(true); // Confirm the menu position var menuNode = iframeDom.getElement('demoMenu'); assertRoughlyEquals( menuNode.offsetTop, node.offsetTop + node.offsetHeight, 20); assertRoughlyEquals(menuNode.offsetLeft, node.offsetLeft, 20); positionMenuCalled = false; // A Tick event is dispatched. mockTimer.dispatchEvent(goog.Timer.TICK); assertFalse('positionMenu() shouldn\'t be called.', positionMenuCalled); // Move the menu button by DOM structure change var p1 = iframeDom.createDom( goog.dom.TagName.P, null, iframeDom.createTextNode('foo')); var p2 = iframeDom.createDom( goog.dom.TagName.P, null, iframeDom.createTextNode('foo')); var p3 = iframeDom.createDom( goog.dom.TagName.P, null, iframeDom.createTextNode('foo')); iframeDom.insertSiblingBefore(p1, node); iframeDom.insertSiblingBefore(p2, node); iframeDom.insertSiblingBefore(p3, node); // Confirm the menu is detached from the button. assertTrue( Math.abs(node.offsetTop + node.offsetHeight - menuNode.offsetTop) > 20); positionMenuCalled = false; // A Tick event is dispatched. mockTimer.dispatchEvent(goog.Timer.TICK); assertTrue('positionMenu() should be called.', positionMenuCalled); // The menu is moved to appropriate position again. assertRoughlyEquals( menuNode.offsetTop, node.offsetTop + node.offsetHeight, 20); // Make the frame page scrollable. var viewportHeight = iframeDom.getViewportSize().height; var footer = iframeDom.getElement('footer'); goog.style.setSize(footer, 1, viewportHeight * 2); // Change the viewport offset. iframeWindow.scrollTo(0, viewportHeight); // A Tick event is dispatched and positionMenu() should be called. positionMenuCalled = false; mockTimer.dispatchEvent(goog.Timer.TICK); assertTrue('positionMenu() should be called.', positionMenuCalled); goog.style.setSize(footer, 1, 1); // Tear down. iframeDom.removeNode(p1); iframeDom.removeNode(p2); iframeDom.removeNode(p3); replacer.reset(); button.dispose(); } /** * Use a different button to position the menu and make sure it does so * correctly. */ function testAlternatePositioningElement() { var node = goog.dom.getElement('demoMenuButton'); menuButton.decorate(node); var posElement = goog.dom.getElement('positionElement'); menuButton.setPositionElement(posElement); // Show the menu. menuButton.setOpen(true); // Confirm the menu position var menuNode = menuButton.getMenu().getElement(); assertRoughlyEquals( menuNode.offsetTop, posElement.offsetTop + posElement.offsetHeight, 20); assertRoughlyEquals(menuNode.offsetLeft, posElement.offsetLeft, 20); } /** * Test forced positioning above the button. */ function testPositioningAboveAnchor() { var node = goog.dom.getElement('demoMenuButton'); menuButton.decorate(node); // Show the menu. menuButton.setAlignMenuToStart(true); // Should get overridden below menuButton.setScrollOnOverflow(true); // Should get overridden below var position = new goog.positioning.MenuAnchoredPosition( menuButton.getElement(), goog.positioning.Corner.TOP_START, /* opt_adjust */ false, /* opt_resize */ false); menuButton.setMenuPosition(position); menuButton.setOpen(true); // Confirm the menu position var buttonBounds = goog.style.getBounds(node); var menuNode = menuButton.getMenu().getElement(); var menuBounds = goog.style.getBounds(menuNode); assertRoughlyEquals(menuBounds.top + menuBounds.height, buttonBounds.top, 3); assertRoughlyEquals(menuBounds.left, buttonBounds.left, 3); // For this test to be valid, the node must have non-trival height. assertRoughlyEquals(node.offsetHeight, 19, 3); } /** * Test forced positioning below the button. */ function testPositioningBelowAnchor() { var node = goog.dom.getElement('demoMenuButton'); menuButton.decorate(node); // Show the menu. menuButton.setAlignMenuToStart(true); // Should get overridden below menuButton.setScrollOnOverflow(true); // Should get overridden below var position = new goog.positioning.MenuAnchoredPosition( menuButton.getElement(), goog.positioning.Corner.BOTTOM_START, /* opt_adjust */ false, /* opt_resize */ false); menuButton.setMenuPosition(position); menuButton.setOpen(true); // Confirm the menu position var buttonBounds = goog.style.getBounds(node); var menuNode = menuButton.getMenu().getElement(); var menuBounds = goog.style.getBounds(menuNode); expectedFailures.expectFailureFor(isWinSafariBefore5()); try { assertRoughlyEquals( menuBounds.top, buttonBounds.top + buttonBounds.height, 3); assertRoughlyEquals(menuBounds.left, buttonBounds.left, 3); } catch (e) { expectedFailures.handleException(e); } // For this test to be valid, the node must have non-trival height. assertRoughlyEquals(node.offsetHeight, 19, 3); } function isWinSafariBefore5() { return goog.userAgent.WINDOWS && goog.userAgent.product.SAFARI && goog.userAgent.product.isVersion(4) && !goog.userAgent.product.isVersion(5); } /** * Tests that space, and only space, fire on key up. */ function testSpaceFireOnKeyUp() { var node = goog.dom.getElement('demoMenuButton'); menuButton.decorate(node); e = new goog.events.Event(goog.events.KeyHandler.EventType.KEY, menuButton); e.preventDefault = goog.testing.recordFunction(); e.keyCode = goog.events.KeyCodes.SPACE; menuButton.handleKeyEvent(e); assertFalse( 'Menu must not have been triggered by Space keypress', menuButton.isOpen()); assertNotNull('Page scrolling is prevented', e.preventDefault.getLastCall()); e = new goog.events.Event(goog.events.EventType.KEYUP, menuButton); e.keyCode = goog.events.KeyCodes.SPACE; menuButton.handleKeyEvent(e); assertTrue( 'Menu must have been triggered by Space keyup', menuButton.isOpen()); menuButton.getMenu().setHighlightedIndex(0); e = new goog.events.Event(goog.events.KeyHandler.EventType.KEY, menuButton); e.keyCode = goog.events.KeyCodes.DOWN; menuButton.handleKeyEvent(e); assertEquals( 'Highlighted menu item must have hanged by Down keypress', 1, menuButton.getMenu().getHighlightedIndex()); menuButton.getMenu().setHighlightedIndex(0); e = new goog.events.Event(goog.events.EventType.KEYUP, menuButton); e.keyCode = goog.events.KeyCodes.DOWN; menuButton.handleKeyEvent(e); assertEquals( 'Highlighted menu item must not have changed by Down keyup', 0, menuButton.getMenu().getHighlightedIndex()); } /** * Tests that preventing the button from closing also prevents the menu from * being hidden. */ function testPreventHide() { var node = goog.dom.getElement('demoMenuButton'); menuButton.decorate(node); menuButton.setDispatchTransitionEvents(goog.ui.Component.State.OPENED, true); // Show the menu. menuButton.setOpen(true); assertTrue('Menu button should be open.', menuButton.isOpen()); assertTrue('Menu should be visible.', menuButton.getMenu().isVisible()); var key = goog.events.listen( menuButton, goog.ui.Component.EventType.CLOSE, function(event) { event.preventDefault(); }); // Try to hide the menu. menuButton.setOpen(false); assertTrue('Menu button should still be open.', menuButton.isOpen()); assertTrue('Menu should still be visible.', menuButton.getMenu().isVisible()); // Remove listener and try again. goog.events.unlistenByKey(key); menuButton.setOpen(false); assertFalse('Menu button should not be open.', menuButton.isOpen()); assertFalse('Menu should not be visible.', menuButton.getMenu().isVisible()); } /** * Tests that opening and closing the menu does not affect how adding or * removing menu items changes the size of the menu. */ function testResizeOnItemAddOrRemove() { var node = goog.dom.getElement('demoMenuButton'); menuButton.decorate(node); var menu = menuButton.getMenu(); // Show the menu. menuButton.setOpen(true); var originalSize = goog.style.getSize(menu.getElement()); // Check that removing an item while the menu is left open correctly changes // the size of the menu. // Remove an item using a method on Menu. var item = menu.removeChildAt(0, true); // Confirm size of menu changed. var afterRemoveSize = goog.style.getSize(menu.getElement()); assertTrue( 'Height of menu must decrease after removing a menu item.', afterRemoveSize.height < originalSize.height); // Check that removing an item while the menu is closed, then opened // (so that reposition is called) correctly changes the size of the menu. // Hide menu. menuButton.setOpen(false); var item2 = menu.removeChildAt(0, true); menuButton.setOpen(true); // Confirm size of menu changed. var afterRemoveAgainSize = goog.style.getSize(menu.getElement()); assertTrue( 'Height of menu must decrease after removing a second menu item.', afterRemoveAgainSize.height < afterRemoveSize.height); // Check that adding an item while the menu is opened, then closed, then // opened, correctly changes the size of the menu. // Add an item, this time using a MenuButton method. menuButton.setOpen(true); menuButton.addItem(item2); menuButton.setOpen(false); menuButton.setOpen(true); // Confirm size of menu changed. var afterAddSize = goog.style.getSize(menu.getElement()); assertTrue( 'Height of menu must increase after adding a menu item.', afterRemoveAgainSize.height < afterAddSize.height); assertEquals( 'Removing and adding back items must not change the height of a menu.', afterRemoveSize.height, afterAddSize.height); // Add back the last item to keep state consistent. menuButton.addItem(item); } /** * Tests that adding and removing items from a menu with scrollOnOverflow is on * correctly resizes the menu. */ function testResizeOnItemAddOrRemoveWithScrollOnOverflow() { var node = goog.dom.getElement('demoMenuButton'); menuButton.decorate(node); var menu = menuButton.getMenu(); // Show the menu. menuButton.setScrollOnOverflow(true); menuButton.setOpen(true); var originalSize = goog.style.getSize(menu.getElement()); // Check that removing an item while the menu is left open correctly changes // the size of the menu. // Remove an item using a method on Menu. var item = menu.removeChildAt(0, true); menuButton.invalidateMenuSize(); menuButton.positionMenu(); // Confirm size of menu changed. var afterRemoveSize = goog.style.getSize(menu.getElement()); assertTrue( 'Height of menu must decrease after removing a menu item.', afterRemoveSize.height < originalSize.height); var item2 = menu.removeChildAt(0, true); menuButton.invalidateMenuSize(); menuButton.positionMenu(); // Confirm size of menu changed. var afterRemoveAgainSize = goog.style.getSize(menu.getElement()); assertTrue( 'Height of menu must decrease after removing a second menu item.', afterRemoveAgainSize.height < afterRemoveSize.height); // Check that adding an item while the menu is opened correctly changes the // size of the menu. menuButton.addItem(item2); menuButton.invalidateMenuSize(); menuButton.positionMenu(); // Confirm size of menu changed. var afterAddSize = goog.style.getSize(menu.getElement()); assertTrue( 'Height of menu must increase after adding a menu item.', afterRemoveAgainSize.height < afterAddSize.height); assertEquals( 'Removing and adding back items must not change the height of a menu.', afterRemoveSize.height, afterAddSize.height); } /** * Try rendering the menu as a sibling rather than as a child of the dom. This * tests the case when the button is rendered, rather than decorated. */ function testRenderMenuAsSibling() { menuButton.setRenderMenuAsSibling(true); menuButton.addItem(new goog.ui.MenuItem('Menu item 1')); menuButton.addItem(new goog.ui.MenuItem('Menu item 2')); // By default the menu is rendered into the top level dom and the button // is rendered into whatever parent we provide. If we don't provide a // parent then we aren't really testing anything, since both would be, by // default, rendered into the top level dom, and therefore siblings. menuButton.render(goog.dom.getElement('siblingTest')); menuButton.setOpen(true); assertEquals( menuButton.getElement().parentNode, menuButton.getMenu().getElement().parentNode); } /** * Check that we render the menu as a sibling of the menu button, immediately * after the menu button. */ function testRenderMenuAsSiblingForDecoratedButton() { var menu = new goog.ui.Menu(); menu.addChild(new goog.ui.MenuItem('Menu item 1'), true /* render */); menu.addChild(new goog.ui.MenuItem('Menu item 2'), true /* render */); menu.addChild(new goog.ui.MenuItem('Menu item 3'), true /* render */); var menuButton = new goog.ui.MenuButton(); menuButton.setMenu(menu); menuButton.setRenderMenuAsSibling(true); var node = goog.dom.getElement('button1'); menuButton.decorate(node); menuButton.setOpen(true); assertEquals( 'The menu should be rendered immediately after the menu button', goog.dom.getNextElementSibling(menuButton.getElement()), menu.getElement()); assertEquals( 'The menu should be rendered immediately before the next button', goog.dom.getNextElementSibling(menu.getElement()), goog.dom.getElement('button2')); } function testAlignToStartSetter() { assertTrue(menuButton.isAlignMenuToStart()); menuButton.setAlignMenuToStart(false); assertFalse(menuButton.isAlignMenuToStart()); menuButton.setAlignMenuToStart(true); assertTrue(menuButton.isAlignMenuToStart()); } function testScrollOnOverflowSetter() { assertFalse(menuButton.isScrollOnOverflow()); menuButton.setScrollOnOverflow(true); assertTrue(menuButton.isScrollOnOverflow()); menuButton.setScrollOnOverflow(false); assertFalse(menuButton.isScrollOnOverflow()); } /** * Tests that the attached menu has been set to aria-hidden=false explicitly * when the menu is opened. */ function testSetOpenUnsetsAriaHidden() { var node = goog.dom.getElement('demoMenuButton'); menuButton.decorate(node); var menuElem = menuButton.getMenu().getElementStrict(); goog.a11y.aria.setState(menuElem, goog.a11y.aria.State.HIDDEN, true); menuButton.setOpen(true); assertEquals( '', goog.a11y.aria.getState(menuElem, goog.a11y.aria.State.HIDDEN)); }