123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451 |
- // 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.TooltipTest');
- goog.setTestOnly('goog.ui.TooltipTest');
- goog.require('goog.dom');
- goog.require('goog.dom.TagName');
- goog.require('goog.events.Event');
- goog.require('goog.events.EventHandler');
- goog.require('goog.events.EventType');
- goog.require('goog.events.FocusHandler');
- goog.require('goog.html.testing');
- goog.require('goog.math.Coordinate');
- goog.require('goog.positioning.AbsolutePosition');
- goog.require('goog.style');
- goog.require('goog.testing.MockClock');
- goog.require('goog.testing.TestQueue');
- goog.require('goog.testing.events');
- goog.require('goog.testing.jsunit');
- goog.require('goog.ui.PopupBase');
- goog.require('goog.ui.Tooltip');
- goog.require('goog.userAgent');
- /**
- * A subclass of Tooltip that overrides {@code getPositioningStrategy}
- * for testing purposes.
- * @constructor
- */
- function TestTooltip(el, text, dom) {
- goog.ui.Tooltip.call(this, el, text, dom);
- }
- goog.inherits(TestTooltip, goog.ui.Tooltip);
- /** @override */
- TestTooltip.prototype.getPositioningStrategy = function() {
- return new goog.positioning.AbsolutePosition(13, 17);
- };
- var tt, clock, handler, eventQueue, dom;
- // Allow positions to be off by one in gecko as it reports scrolling
- // offsets in steps of 2.
- var ALLOWED_OFFSET = goog.userAgent.GECKO ? 1 : 0;
- function setUp() {
- // We get access denied error when accessing the iframe in IE on the farm
- // as IE doesn't have the same window size issues as firefox on the farm
- // we bypass the iframe and use the current document instead.
- if (goog.userAgent.EDGE_OR_IE) {
- dom = goog.dom.getDomHelper(document);
- } else {
- var frame = document.getElementById('testframe');
- var doc = goog.dom.getFrameContentDocument(frame);
- dom = goog.dom.getDomHelper(doc);
- }
- // Host elements in fixed size iframe to avoid window size problems when
- // running under Selenium.
- dom.getDocument().body.innerHTML = '<p id="notpopup">Content</p>' +
- '<p id="hovertarget">Hover Here For Popup</p>' +
- '<p id="second">Secondary target</p>';
- tt = new goog.ui.Tooltip(undefined, undefined, dom);
- tt.setElement(
- dom.createDom(
- goog.dom.TagName.DIV, {id: 'popup', style: 'visibility:hidden'},
- 'Hello'));
- clock = new goog.testing.MockClock(true);
- eventQueue = new goog.testing.TestQueue();
- handler = new goog.events.EventHandler(eventQueue);
- handler.listen(tt, goog.ui.PopupBase.EventType.SHOW, eventQueue.enqueue);
- handler.listen(tt, goog.ui.PopupBase.EventType.HIDE, eventQueue.enqueue);
- }
- function tearDown() {
- // tooltip needs to be hidden as well as disposed of so that it doesn't
- // leave global state hanging around to trip up other tests.
- tt.onHide();
- tt.dispose();
- clock.uninstall();
- handler.removeAll();
- }
- function testConstructor() {
- var element = tt.getElement();
- assertNotNull('Tooltip should have non-null element', element);
- assertEquals(
- 'Tooltip element should be the DIV we created', dom.getElement('popup'),
- element);
- assertEquals(
- 'Tooltip element should be a child of the document body',
- dom.getDocument().body, element.parentNode);
- }
- function testTooltipShowsAndHides() {
- var hoverTarget = dom.getElement('hovertarget');
- var elsewhere = dom.getElement('notpopup');
- var element = tt.getElement();
- var position = new goog.math.Coordinate(5, 5);
- assertNotNull('Tooltip should have non-null element', element);
- assertEquals(
- 'Initial state should be inactive', goog.ui.Tooltip.State.INACTIVE,
- tt.getState());
- tt.attach(hoverTarget);
- tt.setShowDelayMs(100);
- tt.setHideDelayMs(50);
- goog.testing.events.fireMouseOverEvent(hoverTarget, elsewhere, position);
- assertEquals(goog.ui.Tooltip.State.WAITING_TO_SHOW, tt.getState());
- clock.tick(101);
- assertEquals('visible', tt.getElement().style.visibility);
- assertEquals(
- 'tooltip y position (10px margin below the cursor)', '15px',
- tt.getElement().style.top);
- assertEquals(goog.ui.Tooltip.State.SHOWING, tt.getState());
- assertEquals(goog.ui.PopupBase.EventType.SHOW, eventQueue.dequeue().type);
- assertTrue(eventQueue.isEmpty());
- goog.testing.events.fireMouseOutEvent(hoverTarget, elsewhere);
- assertEquals(goog.ui.Tooltip.State.WAITING_TO_HIDE, tt.getState());
- clock.tick(51);
- assertEquals('hidden', tt.getElement().style.visibility);
- assertEquals(goog.ui.Tooltip.State.INACTIVE, tt.getState());
- assertEquals(goog.ui.PopupBase.EventType.HIDE, eventQueue.dequeue().type);
- assertTrue(eventQueue.isEmpty());
- }
- function testMultipleTargets() {
- var firstTarget = dom.getElement('hovertarget');
- var secondTarget = dom.getElement('second');
- var elsewhere = dom.getElement('notpopup');
- var element = tt.getElement();
- tt.attach(firstTarget);
- tt.attach(secondTarget);
- tt.setShowDelayMs(100);
- tt.setHideDelayMs(50);
- // Move over first target
- goog.testing.events.fireMouseOverEvent(firstTarget, elsewhere);
- clock.tick(101);
- assertEquals(goog.ui.PopupBase.EventType.SHOW, eventQueue.dequeue().type);
- assertTrue(eventQueue.isEmpty());
- // Move from first to second
- goog.testing.events.fireMouseOutEvent(firstTarget, secondTarget);
- goog.testing.events.fireMouseOverEvent(secondTarget, firstTarget);
- assertEquals(goog.ui.Tooltip.State.UPDATING, tt.getState());
- assertTrue(eventQueue.isEmpty());
- // Move from second to element (before second shows)
- goog.testing.events.fireMouseOutEvent(secondTarget, element);
- goog.testing.events.fireMouseOverEvent(element, secondTarget);
- assertEquals(goog.ui.Tooltip.State.SHOWING, tt.getState());
- assertTrue(eventQueue.isEmpty());
- // Move from element to second, and let it show
- goog.testing.events.fireMouseOutEvent(element, secondTarget);
- goog.testing.events.fireMouseOverEvent(secondTarget, element);
- assertEquals(goog.ui.Tooltip.State.UPDATING, tt.getState());
- clock.tick(101);
- assertEquals(goog.ui.Tooltip.State.SHOWING, tt.getState());
- assertEquals('Anchor should be second target', secondTarget, tt.anchor);
- assertEquals(goog.ui.PopupBase.EventType.HIDE, eventQueue.dequeue().type);
- assertEquals(goog.ui.PopupBase.EventType.SHOW, eventQueue.dequeue().type);
- assertTrue(eventQueue.isEmpty());
- // Move from second to first and then off without first showing
- goog.testing.events.fireMouseOutEvent(secondTarget, firstTarget);
- goog.testing.events.fireMouseOverEvent(firstTarget, secondTarget);
- assertEquals(goog.ui.Tooltip.State.UPDATING, tt.getState());
- goog.testing.events.fireMouseOutEvent(firstTarget, elsewhere);
- assertEquals(goog.ui.Tooltip.State.WAITING_TO_HIDE, tt.getState());
- clock.tick(51);
- assertEquals('hidden', tt.getElement().style.visibility);
- assertEquals(goog.ui.Tooltip.State.INACTIVE, tt.getState());
- assertEquals(goog.ui.PopupBase.EventType.HIDE, eventQueue.dequeue().type);
- assertTrue(eventQueue.isEmpty());
- clock.tick(200);
- // Move from element to second, but detach second before it shows.
- goog.testing.events.fireMouseOutEvent(element, secondTarget);
- goog.testing.events.fireMouseOverEvent(secondTarget, element);
- assertEquals(goog.ui.Tooltip.State.WAITING_TO_SHOW, tt.getState());
- tt.detach(secondTarget);
- clock.tick(200);
- assertEquals(goog.ui.Tooltip.State.INACTIVE, tt.getState());
- assertEquals('Anchor should be second target', secondTarget, tt.anchor);
- assertTrue(eventQueue.isEmpty());
- }
- function testRequireInteraction() {
- var hoverTarget = dom.getElement('hovertarget');
- var elsewhere = dom.getElement('notpopup');
- tt.attach(hoverTarget);
- tt.setShowDelayMs(100);
- tt.setHideDelayMs(50);
- tt.setRequireInteraction(true);
- goog.testing.events.fireMouseOverEvent(hoverTarget, elsewhere);
- clock.tick(101);
- assertEquals(
- 'Tooltip should not show without mouse move event', 'hidden',
- tt.getElement().style.visibility);
- goog.testing.events.fireMouseMoveEvent(hoverTarget);
- goog.testing.events.fireMouseOverEvent(hoverTarget, elsewhere);
- clock.tick(101);
- assertEquals(
- 'Tooltip should show because we had mouse move event', 'visible',
- tt.getElement().style.visibility);
- goog.testing.events.fireMouseOutEvent(hoverTarget, elsewhere);
- clock.tick(51);
- assertEquals('hidden', tt.getElement().style.visibility);
- goog.testing.events.fireBrowserEvent(
- new goog.events.Event(goog.events.EventType.FOCUS, hoverTarget));
- clock.tick(101);
- assertEquals(
- 'Tooltip should show because we had focus event', 'visible',
- tt.getElement().style.visibility);
- goog.testing.events.fireBrowserEvent(
- new goog.events.Event(goog.events.EventType.BLUR, hoverTarget));
- clock.tick(51);
- assertEquals('hidden', tt.getElement().style.visibility);
- goog.testing.events.fireMouseMoveEvent(hoverTarget);
- goog.testing.events.fireMouseOverEvent(hoverTarget, elsewhere);
- goog.testing.events.fireMouseOutEvent(hoverTarget, elsewhere);
- goog.testing.events.fireMouseOverEvent(hoverTarget, elsewhere);
- clock.tick(101);
- assertEquals(
- 'A cancelled trigger should also cancel the seen interaction', 'hidden',
- tt.getElement().style.visibility);
- }
- function testDispose() {
- var element = tt.getElement();
- tt.dispose();
- assertTrue('Tooltip should have been disposed of', tt.isDisposed());
- assertNull(
- 'Tooltip element reference should have been nulled out', tt.getElement());
- assertNotEquals(
- 'Tooltip element should not be a child of the body', document.body,
- element.parentNode);
- }
- function testNested() {
- var ttNested;
- tt.getElement().appendChild(
- dom.createDom(goog.dom.TagName.SPAN, {id: 'nested'}, 'Goodbye'));
- ttNested = new goog.ui.Tooltip(undefined, undefined, dom);
- ttNested.setElement(
- dom.createDom(goog.dom.TagName.DIV, {id: 'nestedPopup'}, 'hi'));
- tt.setShowDelayMs(100);
- tt.setHideDelayMs(50);
- ttNested.setShowDelayMs(75);
- ttNested.setHideDelayMs(25);
- var nestedAnchor = dom.getElement('nested');
- var hoverTarget = dom.getElement('hovertarget');
- var outerTooltip = dom.getElement('popup');
- var innerTooltip = dom.getElement('nestedPopup');
- var elsewhere = dom.getElement('notpopup');
- ttNested.attach(nestedAnchor);
- tt.attach(hoverTarget);
- // Test mouse into, out of nested tooltip
- goog.testing.events.fireMouseOverEvent(hoverTarget, elsewhere);
- clock.tick(101);
- goog.testing.events.fireMouseOutEvent(hoverTarget, outerTooltip);
- goog.testing.events.fireMouseOverEvent(outerTooltip, hoverTarget);
- clock.tick(51);
- assertEquals('visible', tt.getElement().style.visibility);
- goog.testing.events.fireMouseOutEvent(outerTooltip, nestedAnchor);
- goog.testing.events.fireMouseOverEvent(nestedAnchor, outerTooltip);
- clock.tick(76);
- assertEquals('visible', tt.getElement().style.visibility);
- assertEquals('visible', ttNested.getElement().style.visibility);
- goog.testing.events.fireMouseOutEvent(nestedAnchor, outerTooltip);
- goog.testing.events.fireMouseOverEvent(outerTooltip, nestedAnchor);
- clock.tick(100);
- assertEquals('visible', tt.getElement().style.visibility);
- assertEquals('hidden', ttNested.getElement().style.visibility);
- // Go back in nested tooltip and then out through tooltip element.
- goog.testing.events.fireMouseOutEvent(outerTooltip, nestedAnchor);
- goog.testing.events.fireMouseOverEvent(nestedAnchor, outerTooltip);
- clock.tick(76);
- goog.testing.events.fireMouseOutEvent(nestedAnchor, innerTooltip);
- goog.testing.events.fireMouseOverEvent(innerTooltip, nestedAnchor);
- clock.tick(15);
- assertEquals('visible', tt.getElement().style.visibility);
- assertEquals('visible', ttNested.getElement().style.visibility);
- goog.testing.events.fireMouseOutEvent(innerTooltip, elsewhere);
- clock.tick(26);
- assertEquals('hidden', ttNested.getElement().style.visibility);
- clock.tick(51);
- assertEquals('hidden', tt.getElement().style.visibility);
- // Test with focus
- goog.testing.events.fireBrowserEvent(
- new goog.events.Event(goog.events.EventType.FOCUS, hoverTarget));
- clock.tick(101);
- goog.testing.events.fireBrowserEvent(
- new goog.events.Event(goog.events.EventType.BLUR, hoverTarget));
- goog.testing.events.fireBrowserEvent(
- new goog.events.Event(goog.events.EventType.FOCUS, nestedAnchor));
- clock.tick(76);
- assertEquals('visible', tt.getElement().style.visibility);
- assertEquals('visible', ttNested.getElement().style.visibility);
- goog.testing.events.fireBrowserEvent(
- new goog.events.Event(goog.events.EventType.BLUR, nestedAnchor));
- goog.testing.events.fireBrowserEvent(
- new goog.events.Event(goog.events.EventType.FOCUS, hoverTarget));
- clock.tick(26);
- assertEquals('visible', tt.getElement().style.visibility);
- assertEquals('hidden', ttNested.getElement().style.visibility);
- ttNested.onHide();
- ttNested.dispose();
- }
- function testPosition() {
- dom.getDocument().body.style.paddingBottom = '150%'; // force scrollbar
- var scrollEl = dom.getDocumentScrollElement();
- var anchor = dom.getElement('hovertarget');
- var tooltip = new goog.ui.Tooltip(anchor, 'foo');
- tooltip.getElement().style.position = 'absolute';
- tooltip.cursorPosition.x = 100;
- tooltip.cursorPosition.y = 100;
- tooltip.showForElement(anchor);
- assertEquals(
- 'Tooltip should be at cursor position',
- '(110, 110)', // (100, 100) + padding (10, 10)
- goog.style.getPageOffset(tooltip.getElement()).toString());
- scrollEl.scrollTop = 50;
- var offset = goog.style.getPageOffset(tooltip.getElement());
- assertTrue(
- 'Tooltip should be at cursor position when scrolled',
- Math.abs(offset.x - 110) <= ALLOWED_OFFSET); // 100 + padding 10
- assertTrue(
- 'Tooltip should be at cursor position when scrolled',
- Math.abs(offset.y - 110) <= ALLOWED_OFFSET); // 100 + padding 10
- tooltip.dispose();
- dom.getDocument().body.style.paddingTop = '';
- scrollEl.scrollTop = 0;
- }
- function testPositionOverride() {
- var anchor = dom.getElement('hovertarget');
- var tooltip = new TestTooltip(anchor, 'foo', dom);
- tooltip.showForElement(anchor);
- assertEquals(
- 'Tooltip should be at absolute position', '(13, 17)',
- goog.style.getPageOffset(tooltip.getElement()).toString());
- tooltip.dispose();
- }
- function testHtmlContent() {
- tt.setSafeHtml(
- goog.html.testing.newSafeHtmlForTest(
- '<span class="theSpan">Hello</span>'));
- var spanEl = goog.dom.getElementByClass('theSpan', tt.getElement());
- assertEquals('Hello', goog.dom.getTextContent(spanEl));
- }
- function testSetElementNull() {
- tt.setElement(null);
- }
- function testFocusBlurElementsInTooltip() {
- var anchorEl = dom.getElement('hovertarget');
- goog.dom.setFocusableTabIndex(anchorEl, true);
- tt.attach(anchorEl);
- goog.testing.events.fireFocusEvent(anchorEl);
- clock.tick(1000);
- assertEquals('visible', tt.getElement().style.visibility);
- goog.testing.events.fireBlurEvent(anchorEl);
- tt.tooltipFocusHandler_.dispatchEvent(
- goog.events.FocusHandler.EventType.FOCUSIN);
- clock.tick(1000);
- assertEquals('visible', tt.getElement().style.visibility);
- // Run blur on the previous element followed by focus on the element being
- // focused, as would normally happen when focus() is called on an element.
- tt.tooltipFocusHandler_.dispatchEvent(
- goog.events.FocusHandler.EventType.FOCUSOUT);
- tt.tooltipFocusHandler_.dispatchEvent(
- goog.events.FocusHandler.EventType.FOCUSIN);
- clock.tick(1000);
- assertEquals('visible', tt.getElement().style.visibility);
- tt.tooltipFocusHandler_.dispatchEvent(
- goog.events.FocusHandler.EventType.FOCUSOUT);
- clock.tick(1000);
- assertEquals('hidden', tt.getElement().style.visibility);
- }
- function testFocusElementInTooltipThenBackToAnchor() {
- var anchorEl = dom.getElement('hovertarget');
- goog.dom.setFocusableTabIndex(anchorEl, true);
- tt.attach(anchorEl);
- goog.testing.events.fireFocusEvent(anchorEl);
- clock.tick(1000);
- assertEquals('visible', tt.getElement().style.visibility);
- // Run blur on the previous element followed by focus on the element being
- // focused, as would normally happen when focus() is called on an element.
- goog.testing.events.fireBlurEvent(anchorEl);
- tt.tooltipFocusHandler_.dispatchEvent(
- goog.events.FocusHandler.EventType.FOCUSIN);
- clock.tick(1000);
- assertEquals('visible', tt.getElement().style.visibility);
- // Run blur on the previous element followed by focus on the element being
- // focused, as would normally happen when focus() is called on an element.
- tt.tooltipFocusHandler_.dispatchEvent(
- goog.events.FocusHandler.EventType.FOCUSOUT);
- goog.testing.events.fireFocusEvent(anchorEl);
- clock.tick(1000);
- assertEquals('visible', tt.getElement().style.visibility);
- goog.testing.events.fireBlurEvent(anchorEl);
- clock.tick(1000);
- assertEquals('hidden', tt.getElement().style.visibility);
- }
|