// Copyright 2012 Google Inc. 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. package com.google.collide.client.util; import com.google.collide.client.util.HoverController.HoverListener; import com.google.collide.client.util.HoverController.UnhoverListener; import com.google.gwt.junit.client.GWTTestCase; import com.google.gwt.user.client.Timer; import elemental.events.MouseEvent; import elemental.html.Document; import elemental.html.Element; /** * Tests for {@link HoverController}. * */ public class HoverControllerTest extends GWTTestCase { private static class MockHoverListener implements HoverListener, UnhoverListener { private int hoverCalled; private int unhoverCalled; public void assertHoverCount(int expected) { assertEquals(expected, hoverCalled); } public void assertUnhoverCount(int expected) { assertEquals(expected, unhoverCalled); } @Override public void onHover() { hoverCalled++; } @Override public void onUnhover() { unhoverCalled++; } } @Override public String getModuleName() { return "com.google.collide.client.util.UtilTestModule"; } /** * Tests a basic hover/unhover sequence. */ public void testBasicHoverSequence() { final MockHoverListener listener = new MockHoverListener(); final Element[] elems = createAndAttachElements(3); HoverController controller = new HoverController(); controller.setHoverListener(listener); controller.setUnhoverListener(listener); controller.setUnhoverDelay(25); controller.addPartner(elems[0]); controller.addPartner(elems[1]); controller.addPartner(elems[2]); listener.assertHoverCount(0); listener.assertUnhoverCount(0); // Mouseover an element. onHover() called synchronously. mouseover(elems[1]); listener.assertHoverCount(1); listener.assertUnhoverCount(0); // Mouseout an element. onUnhover() is not called because we are still in // the unhover delay. mouseout(elems[2]); listener.assertHoverCount(1); listener.assertUnhoverCount(0); // Wait for the unhover delay. delayTestFinish(1000); new Timer() { @Override public void run() { listener.assertHoverCount(1); listener.assertUnhoverCount(1); // Cleanup. detachElements(elems); finishTest(); } }.schedule(100); } /** * Tests that once we start hovering, subsequent mouseover events do not call * {@link MockHoverListener#onHover()}. */ public void testMouseoverWhileHovering() { final MockHoverListener listener = new MockHoverListener(); final Element[] elems = createAndAttachElements(3); HoverController controller = new HoverController(); controller.setHoverListener(listener); controller.setUnhoverListener(listener); controller.setUnhoverDelay(25); controller.addPartner(elems[0]); controller.addPartner(elems[1]); controller.addPartner(elems[2]); listener.assertHoverCount(0); listener.assertUnhoverCount(0); // Mouseover an element. onHover() is called synchronously. mouseover(elems[1]); listener.assertHoverCount(1); listener.assertUnhoverCount(0); // Mouseover another element. mouseover(elems[2]); listener.assertHoverCount(1); listener.assertUnhoverCount(0); // Mouseover the same element. mouseover(elems[2]); listener.assertHoverCount(1); listener.assertUnhoverCount(0); // Cleanup. detachElements(elems); } /** * Tests that if the user mouses over an element after mousing out of an * element, but before the unhover delay fires, then * {@link UnhoverListener#onUnhover()} is not called. */ public void testMouseoverWithinUnhoverDelay() { final MockHoverListener listener = new MockHoverListener(); final Element[] elems = createAndAttachElements(3); HoverController controller = new HoverController(); controller.setHoverListener(listener); controller.setUnhoverListener(listener); controller.setUnhoverDelay(25); controller.addPartner(elems[0]); controller.addPartner(elems[1]); controller.addPartner(elems[2]); listener.assertHoverCount(0); listener.assertUnhoverCount(0); // mouseover an element. onHover() called synchronously. mouseover(elems[1]); listener.assertHoverCount(1); listener.assertUnhoverCount(0); // mouseout an element. onUnhover() is not called because we are still in // the unhover delay. mouseout(elems[2]); listener.assertHoverCount(1); listener.assertUnhoverCount(0); // mouseover within the delay. onUnhover() is cancelled. mouseover(elems[2]); listener.assertHoverCount(1); listener.assertUnhoverCount(0); // Verify unhover is never called. delayTestFinish(1000); new Timer() { @Override public void run() { listener.assertHoverCount(1); listener.assertUnhoverCount(0); // Cleanup. detachElements(elems); finishTest(); } }.schedule(100); } /** * Tests that nothing breaks if no listeners are set. */ public void testNoListeners() { final Element[] elems = createAndAttachElements(3); HoverController controller = new HoverController(); controller.setUnhoverDelay(0); controller.addPartner(elems[0]); controller.addPartner(elems[1]); controller.addPartner(elems[2]); // mouseover an element. mouseover(elems[1]); // mouseout an element. mouseout(elems[2]); // Cleanup. detachElements(elems); } /** * Tests that setting the unhover delay to a negative value prevents * {@link UnhoverListener#onUnhover()} from being called. */ public void testSetUnhoverDelayNevagtive() { final MockHoverListener listener = new MockHoverListener(); final Element[] elems = createAndAttachElements(3); HoverController controller = new HoverController(); controller.setHoverListener(listener); controller.setUnhoverListener(listener); controller.setUnhoverDelay(-1); controller.addPartner(elems[0]); controller.addPartner(elems[1]); controller.addPartner(elems[2]); listener.assertHoverCount(0); listener.assertUnhoverCount(0); // mouseover an element. onHover() called synchronously. mouseover(elems[1]); listener.assertHoverCount(1); listener.assertUnhoverCount(0); // mouseout an element. onUnhover() not called. mouseout(elems[2]); listener.assertHoverCount(1); listener.assertUnhoverCount(0); // Verify unhover is never called. delayTestFinish(1000); new Timer() { @Override public void run() { listener.assertHoverCount(1); listener.assertUnhoverCount(0); // Cleanup. detachElements(elems); finishTest(); } }.schedule(100); } /** * Tests that setting the unhover delay to zero forces * {@link UnhoverListener#onUnhover()} to be called synchronously. */ public void testSetUnhoverDelayZero() { final MockHoverListener listener = new MockHoverListener(); final Element[] elems = createAndAttachElements(3); HoverController controller = new HoverController(); controller.setHoverListener(listener); controller.setUnhoverListener(listener); controller.setUnhoverDelay(0); controller.addPartner(elems[0]); controller.addPartner(elems[1]); controller.addPartner(elems[2]); listener.assertHoverCount(0); listener.assertUnhoverCount(0); // mouseover an element. onHover() called synchronously. mouseover(elems[1]); listener.assertHoverCount(1); listener.assertUnhoverCount(0); // mouseout an element. onUnhover() called synchronously. mouseout(elems[2]); listener.assertHoverCount(1); listener.assertUnhoverCount(1); // Cleanup. detachElements(elems); } private Element[] createAndAttachElements(int count) { Document doc = Elements.getDocument(); Element[] elems = new Element[count]; for (int i = 0; i < count; i++) { Element elem = doc.createDivElement(); doc.getBody().appendChild(elem); elems[i] = elem; } return elems; } private void mouseevent(Element target, String type) { MouseEvent evt = (MouseEvent) Elements.getDocument().createEvent(Document.Event.MOUSE); evt.initMouseEvent(type, true, true, null, 0, 0, 0, 0, 0, false, false, false, false, MouseEvent.Button.PRIMARY, null); target.dispatchEvent(evt); } private void mouseout(Element target) { mouseevent(target, "mouseout"); } private void mouseover(Element target) { mouseevent(target, "mouseover"); } private void detachElements(Element[] elems) { for (Element elem : elems) { elem.removeFromParent(); } } }