/* * Copyright (C) 2016 Red Hat, Inc. and/or its affiliates. * * 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.jboss.errai.common.client.util; import org.jboss.errai.common.client.dom.HTMLElement; import org.jboss.errai.common.client.ui.ElementWrapperWidget; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.user.client.ui.HasValue; /** * Contains utility methods for intercepting calls to add and remove web browser event listeners necessary for tests * with certain versions of HTMLUnit. * * @author Max Barkley <mbarkley@redhat.com> */ public abstract class EventTestingUtil { private EventTestingUtil() {} /** * This is a really disgusting workaround for the inability to * dispatch native browser events in the version of HtmlUnit currently * bundled in gwt-dev. * * What does this do? * This replaces "addEventListener" and "removeEventListener" * in the HTMLElement prototype with functions that intercept * and store registered listeners. * * Why does it do it? * So that subsequent calls to "invokeEventListeners" can * manually call any functions added with "addEventListener". * * In short because we cannot dispatch browser events, to test * binding of native elements we must store and then manually invoke * all event listeners. */ public static native void setupAddEventListenerInterceptor() /*-{ console.log("Setting up event listener interceptors."); function ListenerMap() { var map = new Map(); this.add = function(element, type, listener) { var curList = this.get(element, type); console.debug("Adding listener for " + type + " event in " + element + ". Total of " + (curList.length + 1) + " listeners."); curList.push(listener); }; this.remove = function(element, type, listener) { var listeners = this.get(element, type); var index = listeners.indexOf(listener); if (index > -1) { listeners.splice(index, 1); } }; this.get = function(element, type) { if (map.get(element) === undefined) { map.set(element, new Map()); } if (map.get(element).get(type) === undefined) { map.get(element).set(type, []); } return map.get(element).get(type); }; }; if ($wnd.HTMLElement.prototype._addEventListener === undefined) { listeners = new ListenerMap(); $wnd.HTMLElement.prototype._addEventListener = $wnd.HTMLElement.prototype.addEventListener; $wnd.HTMLElement.prototype._removeEventListener = $wnd.HTMLElement.prototype.removeEventListener; console.log("Replacing addEventListener."); $wnd.HTMLElement.prototype.addEventListener = function(type, listener, capture) { console.debug("Intercepted addEventListener(" + this + ", " + type + ", " + capture + ")"); listeners.add(this, type, listener); this._addEventListener(type, listener, capture); }; console.log("Replacing removeEventListener."); $wnd.HTMLElement.prototype.removeEventListener = function(type, listener, capture) { console.debug("Intercepted removeEventListener for " + this + " with type " + type); listeners.remove(this, type, listener); this._removeEventListener(type, listener, capture); }; } }-*/; @SuppressWarnings({ "unchecked", "rawtypes" }) public static void invokeEventListeners(final HTMLElement element, final String eventType) { invokeEventListeners((Object) element, eventType); if ("change".equals(eventType)) { final ElementWrapperWidget elem = ElementWrapperWidget.getWidget(element); if (elem instanceof HasValue) { ValueChangeEvent.fire(((HasValue) elem), ((HasValue) elem).getValue()); } } } @SuppressWarnings({ "unchecked", "rawtypes" }) public static void invokeEventListeners(final Element element, final String eventType) { invokeEventListeners((Object) element, eventType); if ("change".equals(eventType)) { final ElementWrapperWidget elem = ElementWrapperWidget.getWidget(element); if (elem instanceof HasValue) { ValueChangeEvent.fire(((HasValue) elem), ((HasValue) elem).getValue()); } } } public static void invokeEventListeners(final Object element, final String eventType) { final NativeEvent event = Document.get().createHtmlEvent(eventType, true, true); invokeEventListeners(element, eventType, event); } public static native void invokeEventListeners(Object element, String type, Object evt) /*-{ var foundListeners = listeners.get(element, type); console.debug("Found " + foundListeners.length + " for " + type + " event on element " + element); foundListeners.forEach(function(l) { l(evt); }); }-*/; }