123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446 |
- // 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.PopupMenuTest');
- goog.setTestOnly('goog.ui.PopupMenuTest');
- goog.require('goog.dom');
- goog.require('goog.events.EventHandler');
- goog.require('goog.events.EventType');
- goog.require('goog.events.KeyCodes');
- goog.require('goog.math.Box');
- goog.require('goog.math.Coordinate');
- goog.require('goog.positioning.Corner');
- goog.require('goog.style');
- goog.require('goog.testing.events');
- goog.require('goog.testing.jsunit');
- goog.require('goog.ui.Menu');
- goog.require('goog.ui.MenuItem');
- goog.require('goog.ui.PopupMenu');
- var anchor;
- var menu;
- var menuitem;
- // Event handler
- var handler;
- var showPopup;
- var beforeShowPopupCalled;
- var popup;
- function setUp() {
- anchor = goog.dom.getElement('popup-anchor');
- menu = goog.dom.getElement('menu');
- menuitem1 = goog.dom.getElement('menuitem_1');
- menuitem3 = goog.dom.getElement('menuitem_3');
- handler = new goog.events.EventHandler();
- popup = new goog.ui.PopupMenu();
- }
- function tearDown() {
- handler.dispose();
- popup.dispose();
- }
- /**
- * Asserts properties of {@code target} matches the expected value.
- *
- * @param {Object} target The target specifying how the popup menu should be
- * attached to an anchor.
- * @param {Element} expectedElement The expected anchoring element.
- * @param {goog.positioning.Corner} expectedTargetCorner The expected value of
- * the {@code target.targetCorner_} property.
- * @param {goog.positioning.Corner} expectedMenuCorner The expected value of
- * the {@code target.menuCorner_} property.
- * @param {goog.events.EventType} expectedEventType The expected value of the
- * {@code target.eventType_} property.
- * @param {goog.math.Box} expectedMargin The expected value of the
- * {@code target.margin_} property.
- */
- function assertTarget(
- target, expectedElement, expectedTargetCorner, expectedMenuCorner,
- expectedEventType, expectedMargin) {
- var expectedTarget = {
- element_: expectedElement,
- targetCorner_: expectedTargetCorner,
- menuCorner_: expectedMenuCorner,
- eventType_: expectedEventType,
- margin_: expectedMargin
- };
- assertObjectEquals('Target does not match.', expectedTarget, target);
- }
- /**
- * Test menu receives BEFORE_SHOW event before it's displayed.
- */
- function testBeforeShowEvent() {
- popup.render();
- var target = popup.createAttachTarget(anchor);
- popup.attach(anchor);
- function beforeShowPopup(e) {
- // Ensure that the element is not yet visible.
- assertFalse(
- 'The element should not be shown when BEFORE_SHOW event is ' +
- 'being handled',
- goog.style.isElementShown(popup.getElement()));
- // Verify that current anchor is set before dispatching BEFORE_SHOW.
- assertNotNullNorUndefined(popup.getAttachedElement());
- assertEquals(
- 'The attached anchor element is incorrect', target.element_,
- popup.getAttachedElement());
- beforeShowPopupCalled = true;
- return showPopup;
- };
- function onShowPopup(e) {
- assertEquals(
- 'The attached anchor element is incorrect', target.element_,
- popup.getAttachedElement());
- };
- handler.listen(popup, goog.ui.Menu.EventType.BEFORE_SHOW, beforeShowPopup);
- handler.listen(popup, goog.ui.Menu.EventType.SHOW, onShowPopup);
- beforeShowPopupCalled = false;
- showPopup = false;
- popup.showMenu(target, 0, 0);
- assertTrue(
- 'BEFORE_SHOW event handler should be called on #showMenu',
- beforeShowPopupCalled);
- assertFalse(
- 'The element should not be shown when BEFORE_SHOW handler ' +
- 'returned false',
- goog.style.isElementShown(popup.getElement()));
- beforeShowPopupCalled = false;
- showPopup = true;
- popup.showMenu(target, 0, 0);
- assertTrue(
- 'The element should be shown when BEFORE_SHOW handler ' +
- 'returned true',
- goog.style.isElementShown(popup.getElement()));
- }
- /**
- * Test the behavior of {@link PopupMenu.isAttachTarget}.
- */
- function testIsAttachTarget() {
- popup.render();
- // Before 'attach' is called.
- assertFalse(
- 'Menu should not be attached to the element',
- popup.isAttachTarget(anchor));
- popup.attach(anchor);
- assertTrue(
- 'Menu should be attached to the anchor', popup.isAttachTarget(anchor));
- popup.detach(anchor);
- assertFalse(
- 'Menu is expected to be detached from the element',
- popup.isAttachTarget(anchor));
- }
- /**
- * Tests the behavior of {@link PopupMenu.createAttachTarget}.
- */
- function testCreateAttachTarget() {
- // Randomly picking parameters.
- var targetCorner = goog.positioning.Corner.TOP_END;
- var menuCorner = goog.positioning.Corner.BOTTOM_LEFT;
- var contextMenu = false; // Show menu on mouse down event.
- var margin = new goog.math.Box(0, 10, 5, 25);
- // Simply setting the required parameters.
- var target = popup.createAttachTarget(anchor);
- assertTrue(popup.isAttachTarget(anchor));
- assertTarget(
- target, anchor, undefined, undefined, goog.events.EventType.MOUSEDOWN,
- undefined);
- // Creating another target with all the parameters.
- target = popup.createAttachTarget(
- anchor, targetCorner, menuCorner, contextMenu, margin);
- assertTrue(popup.isAttachTarget(anchor));
- assertTarget(
- target, anchor, targetCorner, menuCorner, goog.events.EventType.MOUSEDOWN,
- margin);
- // Finally, switch up the 'contextMenu'
- target = popup.createAttachTarget(
- anchor, undefined, undefined, true /*opt_contextMenu*/, undefined);
- assertTarget(
- target, anchor, undefined, undefined, goog.events.EventType.CONTEXTMENU,
- undefined);
- }
- /**
- * Tests the behavior of {@link PopupMenu.getAttachTarget}.
- */
- function testGetAttachTarget() {
- popup.render();
- // Before the menu is attached to the anchor.
- var target = popup.getAttachTarget(anchor);
- assertTrue(
- 'Not expecting a target before the element is attach to the menu',
- target == null);
- // Randomly picking parameters.
- var targetCorner = goog.positioning.Corner.TOP_END;
- var menuCorner = goog.positioning.Corner.BOTTOM_LEFT;
- var contextMenu = false; // Show menu on mouse down event.
- var margin = new goog.math.Box(0, 10, 5, 25);
- popup.attach(anchor, targetCorner, menuCorner, contextMenu, margin);
- target = popup.getAttachTarget(anchor);
- assertTrue(
- 'Failed to get target after attaching element to menu', target != null);
- // Make sure we got the right target back.
- assertTarget(
- target, anchor, targetCorner, menuCorner, goog.events.EventType.MOUSEDOWN,
- margin);
- }
- function testSmallViewportSliding() {
- popup.render();
- popup.getElement().style.position = 'absolute';
- popup.getElement().style.outline = '1px solid blue';
- var item = new goog.ui.MenuItem('Test Item');
- popup.addChild(item, true);
- item.getElement().style.overflow = 'hidden';
- var viewport = goog.style.getClientViewportElement();
- var viewportRect = goog.style.getVisibleRectForElement(viewport);
- var middlePos = Math.floor((viewportRect.right - viewportRect.left) / 2);
- var leftwardPos = Math.floor((viewportRect.right - viewportRect.left) / 3);
- var rightwardPos =
- Math.floor((viewportRect.right - viewportRect.left) / 3 * 2);
- // Can interpret these positions as widths relative to the viewport as well.
- var smallWidth = leftwardPos;
- var mediumWidth = middlePos;
- var largeWidth = rightwardPos;
- // Test small menu first. This should be small enough that it will display
- // its upper left corner where we tell it to in all three positions.
- popup.getElement().style.width = smallWidth + 'px';
- var target = popup.createAttachTarget(anchor);
- popup.attach(anchor);
- popup.showMenu(target, leftwardPos, 0);
- assertObjectEquals(
- 'Popup in wrong position: small size, leftward pos',
- new goog.math.Coordinate(leftwardPos, 0),
- goog.style.getPosition(popup.getElement()));
- popup.showMenu(target, middlePos, 0);
- assertObjectEquals(
- 'Popup in wrong position: small size, middle pos',
- new goog.math.Coordinate(middlePos, 0),
- goog.style.getPosition(popup.getElement()));
- popup.showMenu(target, rightwardPos, 0);
- assertObjectEquals(
- 'Popup in wrong position: small size, rightward pos',
- new goog.math.Coordinate(rightwardPos, 0),
- goog.style.getPosition(popup.getElement()));
- // Test medium menu next. This should display with its upper left corner
- // at the target when leftward and middle, but on the right it should
- // position its upper right corner at the target instead.
- popup.getElement().style.width = mediumWidth + 'px';
- popup.showMenu(target, leftwardPos, 0);
- assertObjectEquals(
- 'Popup in wrong position: medium size, leftward pos',
- new goog.math.Coordinate(leftwardPos, 0),
- goog.style.getPosition(popup.getElement()));
- popup.showMenu(target, middlePos, 0);
- assertObjectEquals(
- 'Popup in wrong position: medium size, middle pos',
- new goog.math.Coordinate(middlePos, 0),
- goog.style.getPosition(popup.getElement()));
- popup.showMenu(target, rightwardPos, 0);
- assertObjectEquals(
- 'Popup in wrong position: medium size, rightward pos',
- new goog.math.Coordinate(rightwardPos - mediumWidth, 0),
- goog.style.getPosition(popup.getElement()));
- // Test large menu next. This should display with its upper left corner at
- // the target when leftward, and its upper right corner at the target when
- // rightward, but right in the middle neither corner can be at the target and
- // keep the entire menu onscreen, so it should place its upper right corner
- // at the very right edge of the viewport.
- popup.getElement().style.width = largeWidth + 'px';
- popup.showMenu(target, leftwardPos, 0);
- assertObjectEquals(
- 'Popup in wrong position: large size, leftward pos',
- new goog.math.Coordinate(leftwardPos, 0),
- goog.style.getPosition(popup.getElement()));
- popup.showMenu(target, middlePos, 0);
- assertObjectEquals(
- 'Popup in wrong position: large size, middle pos',
- new goog.math.Coordinate(
- viewportRect.right - viewportRect.left - largeWidth, 0),
- goog.style.getPosition(popup.getElement()));
- popup.showMenu(target, rightwardPos, 0);
- assertObjectEquals(
- 'Popup in wrong position: large size, rightward pos',
- new goog.math.Coordinate(rightwardPos - largeWidth, 0),
- goog.style.getPosition(popup.getElement()));
- // Make sure that the menu still displays correctly if we give the target
- // a target corner. We can't set the overflow policy in that case, but it
- // should still display.
- popup.detach(anchor);
- anchor.style.position = 'absolute';
- anchor.style.left = '24px';
- anchor.style.top = '24px';
- var targetCorner = goog.positioning.Corner.TOP_END;
- target = popup.createAttachTarget(anchor, targetCorner);
- popup.attach(anchor, targetCorner);
- popup.getElement().style.width = smallWidth + 'px';
- popup.showMenu(target, leftwardPos, 0);
- assertObjectEquals(
- 'Popup in wrong position: small size, leftward pos, with target corner',
- new goog.math.Coordinate(24, 24),
- goog.style.getPosition(popup.getElement()));
- }
- /**
- * Tests that the menu is shown if the SPACE or ENTER keys are pressed, and that
- * none of the menu items are highlighted (PopupMenu.highlightedIndex == -1).
- */
- function testKeyboardEventsShowMenu() {
- popup.decorate(menu);
- popup.attach(anchor);
- popup.hide();
- assertFalse(popup.isVisible());
- goog.testing.events.fireKeySequence(anchor, goog.events.KeyCodes.SPACE);
- assertTrue(popup.isVisible());
- assertEquals(-1, popup.getHighlightedIndex());
- popup.hide();
- assertFalse(popup.isVisible());
- goog.testing.events.fireKeySequence(anchor, goog.events.KeyCodes.ENTER);
- assertTrue(popup.isVisible());
- assertEquals(-1, popup.getHighlightedIndex());
- }
- /**
- * Tests that the menu is shown and the first item is highlighted if the DOWN
- * key is pressed.
- */
- function testDownKey() {
- popup.decorate(menu);
- popup.attach(anchor);
- popup.hide();
- assertFalse(popup.isVisible());
- goog.testing.events.fireKeySequence(anchor, goog.events.KeyCodes.DOWN);
- assertTrue(popup.isVisible());
- assertEquals(0, popup.getHighlightedIndex());
- }
- /**
- * Tests activation of menu items by keyboard.
- */
- function testMenuItemKeyboardActivation() {
- popup.decorate(menu);
- popup.attach(anchor);
- // Check that if the ESC key is pressed the focus is on
- // the anchor element.
- goog.testing.events.fireKeySequence(menu, goog.events.KeyCodes.ESC);
- assertEquals(anchor, document.activeElement);
- var menuitemListenerFired = false;
- function onMenuitemAction(event) {
- if (event.keyCode == goog.events.KeyCodes.SPACE ||
- event.keyCode == goog.events.KeyCodes.ENTER) {
- menuitemListenerFired = true;
- }
- };
- handler.listen(menuitem1, goog.events.EventType.KEYDOWN, onMenuitemAction);
- // Simulate opening a menu using the DOWN key, and pressing the SPACE/ENTER
- // key in order to activate the first menuitem.
- goog.testing.events.fireKeySequence(anchor, goog.events.KeyCodes.DOWN);
- goog.testing.events.fireKeySequence(menu, goog.events.KeyCodes.SPACE);
- assertTrue(menuitemListenerFired);
- menuitemListenerFired = false;
- goog.testing.events.fireKeySequence(anchor, goog.events.KeyCodes.DOWN);
- goog.testing.events.fireKeySequence(menu, goog.events.KeyCodes.ENTER);
- assertTrue(menuitemListenerFired);
- // Make sure the menu item's listener doesn't fire for any key.
- menuitemListenerFired = false;
- goog.testing.events.fireKeySequence(anchor, goog.events.KeyCodes.DOWN);
- goog.testing.events.fireKeySequence(menu, goog.events.KeyCodes.SHIFT);
- assertFalse(menuitemListenerFired);
- // Simulate opening menu and moving down to the third menu item using the
- // DOWN key, and then activating it using the SPACE key.
- menuitemListenerFired = false;
- handler.listen(menuitem3, goog.events.EventType.KEYDOWN, onMenuitemAction);
- goog.testing.events.fireKeySequence(anchor, goog.events.KeyCodes.DOWN);
- goog.testing.events.fireKeySequence(anchor, goog.events.KeyCodes.DOWN);
- goog.testing.events.fireKeySequence(anchor, goog.events.KeyCodes.DOWN);
- goog.testing.events.fireKeySequence(menu, goog.events.KeyCodes.SPACE);
- assertTrue(menuitemListenerFired);
- }
- /**
- * Tests that a context menu isn't shown if the SPACE or ENTER keys are pressed.
- */
- function testContextMenuKeyboard() {
- popup.attach(anchor, null, null, true);
- popup.hide();
- assertFalse(popup.isVisible());
- goog.testing.events.fireKeySequence(anchor, goog.events.KeyCodes.SPACE);
- assertFalse(popup.isVisible());
- goog.testing.events.fireKeySequence(anchor, goog.events.KeyCodes.ENTER);
- assertFalse(popup.isVisible());
- }
- /**
- * Tests that there is no crash when hitting a key when no menu item is
- * highlighted.
- */
- function testKeyPressWithNoHighlightedItem() {
- popup.decorate(menu);
- popup.attach(anchor);
- goog.testing.events.fireKeySequence(anchor, goog.events.KeyCodes.SPACE);
- assertTrue(popup.isVisible());
- try {
- goog.testing.events.fireKeySequence(menu, goog.events.KeyCodes.SPACE);
- } catch (e) {
- fail(
- 'Crash attempting to reference null selected menu item after ' +
- 'keyboard event.');
- }
- }
|