/** * Copyright 2010 Google Inc. * * 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 org.waveprotocol.wave.client.wavepanel.event; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.junit.client.GWTTestCase; import com.google.gwt.user.client.ui.RootPanel; import org.waveprotocol.wave.client.common.safehtml.EscapeUtils; import org.waveprotocol.wave.client.common.safehtml.SafeHtml; import org.waveprotocol.wave.client.wavepanel.event.EventDispatcherPanel.HandlerCollection; import org.waveprotocol.wave.model.util.Pair; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Tests for {@link EventDispatcherPanel}. * */ @SuppressWarnings("unchecked") // Arrays.asList() of generics. public class EventDispatcherPanelGwtTest extends GWTTestCase { // Mock event type static class MyEvent { } // Mock handler type static class MyHandler { } /** * Mock handler collection that records handler invocations, and can also be * programmed with behavior. */ static class MockHandlers extends HandlerCollection<MyEvent, MyHandler> { private final List<Pair<MyHandler, Element>> invoked = new ArrayList<Pair<MyHandler, Element>>(); private final Set<MyHandler> propagationStoppers = new HashSet<MyHandler>(); MockHandlers(Element top) { super(top, "myevent"); } @Override void registerGwtHandler() { // Do nothing. } @Override boolean dispatch(MyEvent event, Element context, MyHandler handler) { invoked.add(Pair.of(handler, context)); return propagationStoppers.contains(handler); } /** Makes a handler stop the propagation of an event. */ void stopOn(MyHandler handler) { propagationStoppers.add(handler); } /** @return the handler invocations that occurred during dispatch. */ List<Pair<MyHandler, Element>> getInvoked() { return invoked; } } // Some elements in the sample DOM. private Element top; private Element foo; private Element bar; // Sample event handlers. private MyHandler fooHandler; private MyHandler barHandler; /** Event handler collection being tested. */ private MockHandlers handlers; @Override public String getModuleName() { return "org.waveprotocol.wave.client.wavepanel.event.Tests"; } @Override protected void gwtSetUp() { SafeHtml dom = EscapeUtils.fromSafeConstant("" + // \u2620 "<div id='base' kind='base'>" + // \u2620 " <div>" + // \u2620 " <div kind='foo' id='foo'>" + // \u2620 " <div kind='unused'>" + // \u2620 " <div kind='bar' id='bar'>" + // \u2620 " <div id='source'></div>" + // \u2620 " </div>" + // \u2620 " </div>" + // \u2620 " </div>" + // \u2620 " </div>" + // \u2620 "</div>"); top = load(dom); foo = Document.get().getElementById("foo"); bar = Document.get().getElementById("bar"); // Register some handlers. handlers = new MockHandlers(top); fooHandler = new MyHandler(); barHandler = new MyHandler(); handlers.register("foo", fooHandler); handlers.register("bar", barHandler); } @Override protected void gwtTearDown() { top.removeFromParent(); } /** Injects some HTML into the DOM. */ private static Element load(SafeHtml html) { Element container = Document.get().createDivElement(); container.setInnerHTML(html.asString()); Element content = container.getFirstChildElement(); RootPanel.get().getElement().appendChild(content); return content; } /** Fires a fake browser event through the handler collection. */ private void synthesizeEvent() { // Synthesize an event. Element target = Document.get().getElementById("source"); handlers.dispatch(new MyEvent(), target); } public void testDispatchOrder() { synthesizeEvent(); // Verify that both handlers were called in the right order. assertEquals( Arrays.asList(Pair.of(barHandler, bar), Pair.of(fooHandler, foo)), handlers.getInvoked()); } public void testHandlingStopsBubbling() { // Make the barHandler stop bubbling. handlers.stopOn(barHandler); synthesizeEvent(); // Verify that both handlers were called in the right order. assertEquals(Arrays.asList(Pair.of(barHandler, bar)), handlers.getInvoked()); } public void testGlobalDispatch() { MyHandler globalHandler = new MyHandler(); handlers.register(null, globalHandler); synthesizeEvent(); // Verify that global handler is invoked last. List<?> invoked = handlers.getInvoked(); assertEquals(Pair.of(globalHandler, top), invoked.get(invoked.size() - 1)); } public void testGlobalAndTopKindDoNotInterfere() { MyHandler topHandler = new MyHandler(); MyHandler globalHandler = new MyHandler(); handlers.register("base", topHandler); handlers.register(null, globalHandler); synthesizeEvent(); // Verify that top handler is the last invoked kind handler, then the global // handler. List<Pair<MyHandler, Element>> invoked = handlers.getInvoked(); List<Pair<MyHandler, Element>> end = invoked.subList(invoked.size() - 2, invoked.size()); assertEquals(Arrays.asList(Pair.of(topHandler, top), Pair.of(globalHandler, top)), end); } public void testKindHandlerStoppingBubblingAvoidsGlobalHandler() { MyHandler globalHandler = new MyHandler(); handlers.register(null, globalHandler); handlers.stopOn(fooHandler); // Verify that global handlers is not invoked. assertFalse(handlers.getInvoked().contains(globalHandler)); } public void testEventOnTopElementIsDispatched() { MyHandler topHandler = new MyHandler(); handlers.register("base", topHandler); handlers.dispatch(new MyEvent(), top); assertEquals(Collections.singletonList(Pair.of(topHandler, top)), handlers.getInvoked()); } public void testMultipleGlobalHandlersThrowsException() { handlers.register(null, new MyHandler()); try { handlers.register(null, new MyHandler()); fail(); } catch (IllegalStateException e) { // Expected. } } public void testMultipleHandlersOfSameKindThrowsException() { try { handlers.register("foo", new MyHandler()); fail(); } catch (IllegalStateException e) { // Expected. } } }