sku-pg
Version:
Skulpt is a Javascript implementation of Python 2.x. Python that runs in your browser!
638 lines (544 loc) • 22.2 kB
HTML
<html>
<!--
Copyright 2008 The Closure Library Authors. All Rights Reserved.
Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<!--
@author nicksantos@google.com (Nick Santos)
-->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Closure Unit Tests - goog.ui.MenuButton</title>
<style type='text/css'>
.goog-menu {
position: absolute;
color: #aaa;
}
</style>
<script src="../base.js"></script>
<script>
goog.require('goog.Timer');
goog.require('goog.a11y.aria');
goog.require('goog.a11y.aria.State');
goog.require('goog.dom');
goog.require('goog.events');
goog.require('goog.events.Event');
goog.require('goog.events.EventType');
goog.require('goog.events.KeyCodes');
goog.require('goog.positioning');
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.Menu');
goog.require('goog.ui.MenuButton');
goog.require('goog.ui.MenuItem');
goog.require('goog.userAgent');
goog.require('goog.userAgent.product');
goog.require('goog.userAgent.product.isVersion');
</script>
</head>
<body>
<iframe id="iframe1" src="menubutton_test_frame.html" width="400" height="400">
</iframe>
<div id="positionElement" style="position: absolute; left: 205px"></div>
<script>
var menuButton;
var clonedMenuButtonDom;
var 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) {
this.type = goog.events.KeyHandler.EventType.KEY;
this.keyCode = keyCode;
this.preventDefault = goog.nullFunction;
this.stopPropagation = goog.nullFunction;
}
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));
}
/**
* 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());
}
/**
* 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('p', null, iframeDom.createTextNode('foo'));
var p2 = iframeDom.createDom('p', null, iframeDom.createTextNode('foo'));
var p3 = iframeDom.createDom('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.
*/
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);
}
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());
}
</script>
<p>
Here's a menubutton defined in markup:
</p>
<div id="siblingTest"></div>
<div id="demoMenuButton" class="goog-menu-button">
<div id="demoMenu" class="goog-menu">
<div id='menuItem1' class="goog-menuitem">Annual Report.pdf</div>
<div id='menuItem2' class="goog-menuitem">Quarterly Update.pdf</div>
<div id='menuItem3' class="goog-menuitem">Enemies List.txt</div>
</div>
</div>
<div id="footer"></div>
</body>
</html>