123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803 |
- // 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.
- /**
- * @fileoverview Event Simulation.
- *
- * Utility functions for simulating events at the Closure level. All functions
- * in this package generate events by calling goog.events.fireListeners,
- * rather than interfacing with the browser directly. This is intended for
- * testing purposes, and should not be used in production code.
- *
- * The decision to use Closure events and dispatchers instead of the browser's
- * native events and dispatchers was conscious and deliberate. Native event
- * dispatchers have their own set of quirks and edge cases. Pure JS dispatchers
- * are more robust and transparent.
- *
- * If you think you need a testing mechanism that uses native Event objects,
- * please, please email closure-tech first to explain your use case before you
- * sink time into this.
- *
- * TODO(b/8933952): Migrate to explicitly non-nullable types. At present, many
- * functions in this file expect non-null inputs but do not explicitly
- * indicate this.
- *
- * @author nicksantos@google.com (Nick Santos)
- */
- goog.setTestOnly('goog.testing.events');
- goog.provide('goog.testing.events');
- goog.provide('goog.testing.events.Event');
- goog.require('goog.Disposable');
- goog.require('goog.asserts');
- goog.require('goog.dom.NodeType');
- goog.require('goog.events');
- goog.require('goog.events.BrowserEvent');
- goog.require('goog.events.BrowserFeature');
- goog.require('goog.events.EventTarget');
- goog.require('goog.events.EventType');
- goog.require('goog.events.KeyCodes');
- goog.require('goog.object');
- goog.require('goog.style');
- goog.require('goog.userAgent');
- /**
- * goog.events.BrowserEvent expects an Event so we provide one for JSCompiler.
- *
- * This clones a lot of the functionality of goog.events.Event. This used to
- * use a mixin, but the mixin results in confusing the two types when compiled.
- *
- * @param {string} type Event Type.
- * @param {Object=} opt_target Reference to the object that is the target of
- * this event.
- * @constructor
- * @extends {Event}
- */
- goog.testing.events.Event = function(type, opt_target) {
- this.type = type;
- this.target = /** @type {EventTarget} */ (opt_target || null);
- this.currentTarget = this.target;
- };
- /**
- * Whether to cancel the event in internal capture/bubble processing for IE.
- * @type {boolean}
- * @public
- * @suppress {underscore|visibility} Technically public, but referencing this
- * outside this package is strongly discouraged.
- */
- goog.testing.events.Event.prototype.propagationStopped_ = false;
- /** @override */
- goog.testing.events.Event.prototype.defaultPrevented = false;
- /**
- * Return value for in internal capture/bubble processing for IE.
- * @type {boolean}
- * @public
- * @suppress {underscore|visibility} Technically public, but referencing this
- * outside this package is strongly discouraged.
- */
- goog.testing.events.Event.prototype.returnValue_ = true;
- /** @override */
- goog.testing.events.Event.prototype.stopPropagation = function() {
- this.propagationStopped_ = true;
- };
- /** @override */
- goog.testing.events.Event.prototype.preventDefault = function() {
- this.defaultPrevented = true;
- this.returnValue_ = false;
- };
- /**
- * Asserts an event target exists. This will fail if target is not defined.
- *
- * TODO(nnaze): Gradually add this to the methods in this file, and eventually
- * update the method signatures to not take nullables. See http://b/8961907
- *
- * @param {EventTarget} target A target to assert.
- * @return {!EventTarget} The target, guaranteed to exist.
- * @private
- */
- goog.testing.events.assertEventTarget_ = function(target) {
- return goog.asserts.assert(target, 'EventTarget should be defined.');
- };
- /**
- * A static helper function that sets the mouse position to the event.
- * @param {Event} event A simulated native event.
- * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
- * target's position (if available), otherwise (0, 0).
- * @private
- */
- goog.testing.events.setEventClientXY_ = function(event, opt_coords) {
- if (!opt_coords && event.target &&
- /** @type {!Node} */ (event.target).nodeType ==
- goog.dom.NodeType.ELEMENT) {
- try {
- opt_coords =
- goog.style.getClientPosition(/** @type {!Element} **/ (event.target));
- } catch (ex) {
- // IE sometimes throws if it can't get the position.
- }
- }
- event.clientX = opt_coords ? opt_coords.x : 0;
- event.clientY = opt_coords ? opt_coords.y : 0;
- // Pretend the browser window is at (0, 0).
- event.screenX = event.clientX;
- event.screenY = event.clientY;
- };
- /**
- * Simulates a mousedown, mouseup, and then click on the given event target,
- * with the left mouse button.
- * @param {EventTarget} target The target for the event.
- * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
- * defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
- * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
- * target's position (if available), otherwise (0, 0).
- * @param {Object=} opt_eventProperties Event properties to be mixed into the
- * BrowserEvent.
- * @return {boolean} The returnValue of the sequence: false if preventDefault()
- * was called on any of the events, true otherwise.
- */
- goog.testing.events.fireClickSequence = function(
- target, opt_button, opt_coords, opt_eventProperties) {
- // Fire mousedown, mouseup, and click. Then return the bitwise AND of the 3.
- return !!(
- goog.testing.events.fireMouseDownEvent(
- target, opt_button, opt_coords, opt_eventProperties) &
- goog.testing.events.fireMouseUpEvent(
- target, opt_button, opt_coords, opt_eventProperties) &
- goog.testing.events.fireClickEvent(
- target, opt_button, opt_coords, opt_eventProperties));
- };
- /**
- * Simulates the sequence of events fired by the browser when the user double-
- * clicks the given target.
- * @param {EventTarget} target The target for the event.
- * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
- * target's position (if available), otherwise (0, 0).
- * @param {Object=} opt_eventProperties Event properties to be mixed into the
- * BrowserEvent.
- * @return {boolean} The returnValue of the sequence: false if preventDefault()
- * was called on any of the events, true otherwise.
- */
- goog.testing.events.fireDoubleClickSequence = function(
- target, opt_coords, opt_eventProperties) {
- // Fire mousedown, mouseup, click, mousedown, mouseup, click, dblclick.
- // Then return the bitwise AND of the 7.
- var btn = goog.events.BrowserEvent.MouseButton.LEFT;
- return !!(
- goog.testing.events.fireMouseDownEvent(
- target, btn, opt_coords, opt_eventProperties) &
- goog.testing.events.fireMouseUpEvent(
- target, btn, opt_coords, opt_eventProperties) &
- goog.testing.events.fireClickEvent(
- target, btn, opt_coords, opt_eventProperties) &
- // IE fires a selectstart instead of the second mousedown in a
- // dblclick, but we don't care about selectstart.
- (goog.userAgent.IE ||
- goog.testing.events.fireMouseDownEvent(
- target, btn, opt_coords, opt_eventProperties)) &
- goog.testing.events.fireMouseUpEvent(
- target, btn, opt_coords, opt_eventProperties) &
- // IE doesn't fire the second click in a dblclick.
- (goog.userAgent.IE ||
- goog.testing.events.fireClickEvent(
- target, btn, opt_coords, opt_eventProperties)) &
- goog.testing.events.fireDoubleClickEvent(
- target, opt_coords, opt_eventProperties));
- };
- /**
- * Simulates a complete keystroke (keydown, keypress, and keyup). Note that
- * if preventDefault is called on the keydown, the keypress will not fire.
- *
- * @param {EventTarget} target The target for the event.
- * @param {string|number} keyOrKeyCode The key value or keycode of the key
- * pressed.
- * @param {Object=} opt_eventProperties Event properties to be mixed into the
- * BrowserEvent.
- * @return {boolean} The returnValue of the sequence: false if preventDefault()
- * was called on any of the events, true otherwise.
- */
- goog.testing.events.fireKeySequence = function(
- target, keyOrKeyCode, opt_eventProperties) {
- return goog.testing.events.fireNonAsciiKeySequence(
- target, keyOrKeyCode, keyOrKeyCode, opt_eventProperties);
- };
- /**
- * Simulates a complete keystroke (keydown, keypress, and keyup) when typing
- * a non-ASCII character. Same as fireKeySequence, the keypress will not fire
- * if preventDefault is called on the keydown.
- *
- * @param {EventTarget} target The target for the event.
- * @param {string|number} keyOrKeyCode The key value or keycode of the keydown
- * and keyup events.
- * @param {string|number} keyPressKeyOrKeyCode The key value or keycode of the
- * keypress event.
- * @param {Object=} opt_eventProperties Event properties to be mixed into the
- * BrowserEvent.
- * @return {boolean} The returnValue of the sequence: false if preventDefault()
- * was called on any of the events, true otherwise.
- */
- goog.testing.events.fireNonAsciiKeySequence = function(
- target, keyOrKeyCode, keyPressKeyOrKeyCode, opt_eventProperties) {
- var keydown =
- new goog.testing.events.Event(goog.events.EventType.KEYDOWN, target);
- var keyup =
- new goog.testing.events.Event(goog.events.EventType.KEYUP, target);
- var keypress =
- new goog.testing.events.Event(goog.events.EventType.KEYPRESS, target);
- if (goog.isString(keyOrKeyCode)) {
- keydown.key = keyup.key = /** @type {string} */ (keyOrKeyCode);
- keypress.key = /** @type {string} */ (keyPressKeyOrKeyCode);
- } else {
- keydown.keyCode = keyup.keyCode = /** @type {number} */ (keyOrKeyCode);
- keypress.keyCode = /** @type {number} */ (keyPressKeyOrKeyCode);
- }
- if (opt_eventProperties) {
- goog.object.extend(keydown, opt_eventProperties);
- goog.object.extend(keyup, opt_eventProperties);
- goog.object.extend(keypress, opt_eventProperties);
- }
- // Fire keydown, keypress, and keyup. Note that if the keydown is
- // prevent-defaulted, then the keypress will not fire.
- var result = true;
- if (!goog.testing.events.isBrokenGeckoMacActionKey_(keydown)) {
- result = goog.testing.events.fireBrowserEvent(keydown);
- }
- if (goog.isString(keyOrKeyCode)) {
- if (/** @type {string} */ (keyPressKeyOrKeyCode) != '' && result) {
- result &= goog.testing.events.fireBrowserEvent(keypress);
- }
- } else {
- if (goog.events.KeyCodes.firesKeyPressEvent(
- /** @type {number} */ (keyOrKeyCode), undefined, keydown.shiftKey,
- keydown.ctrlKey, keydown.altKey) &&
- result) {
- result &= goog.testing.events.fireBrowserEvent(keypress);
- }
- }
- return !!(result & goog.testing.events.fireBrowserEvent(keyup));
- };
- /**
- * @param {goog.testing.events.Event} e The event.
- * @return {boolean} Whether this is the Gecko/Mac's Meta-C/V/X, which
- * is broken and requires special handling.
- * @private
- */
- goog.testing.events.isBrokenGeckoMacActionKey_ = function(e) {
- return goog.userAgent.MAC && goog.userAgent.GECKO &&
- (e.keyCode == goog.events.KeyCodes.C ||
- e.keyCode == goog.events.KeyCodes.X ||
- e.keyCode == goog.events.KeyCodes.V) &&
- e.metaKey;
- };
- /**
- * Simulates a mouseenter event on the given target.
- * @param {!EventTarget} target The target for the event.
- * @param {?EventTarget} relatedTarget The related target for the event (e.g.,
- * the node that the mouse is being moved out of).
- * @param {!goog.math.Coordinate=} opt_coords Mouse position. Defaults to
- * event's target's position (if available), otherwise (0, 0).
- * @return {boolean} The returnValue of the event: false if preventDefault() was
- * called on it, true otherwise.
- */
- goog.testing.events.fireMouseEnterEvent = function(
- target, relatedTarget, opt_coords) {
- var mouseenter =
- new goog.testing.events.Event(goog.events.EventType.MOUSEENTER, target);
- mouseenter.relatedTarget = relatedTarget;
- goog.testing.events.setEventClientXY_(mouseenter, opt_coords);
- return goog.testing.events.fireBrowserEvent(mouseenter);
- };
- /**
- * Simulates a mouseleave event on the given target.
- * @param {!EventTarget} target The target for the event.
- * @param {?EventTarget} relatedTarget The related target for the event (e.g.,
- * the node that the mouse is being moved into).
- * @param {!goog.math.Coordinate=} opt_coords Mouse position. Defaults to
- * event's target's position (if available), otherwise (0, 0).
- * @return {boolean} The returnValue of the event: false if preventDefault() was
- * called on it, true otherwise.
- */
- goog.testing.events.fireMouseLeaveEvent = function(
- target, relatedTarget, opt_coords) {
- var mouseleave =
- new goog.testing.events.Event(goog.events.EventType.MOUSELEAVE, target);
- mouseleave.relatedTarget = relatedTarget;
- goog.testing.events.setEventClientXY_(mouseleave, opt_coords);
- return goog.testing.events.fireBrowserEvent(mouseleave);
- };
- /**
- * Simulates a mouseover event on the given target.
- * @param {EventTarget} target The target for the event.
- * @param {EventTarget} relatedTarget The related target for the event (e.g.,
- * the node that the mouse is being moved out of).
- * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
- * target's position (if available), otherwise (0, 0).
- * @return {boolean} The returnValue of the event: false if preventDefault() was
- * called on it, true otherwise.
- */
- goog.testing.events.fireMouseOverEvent = function(
- target, relatedTarget, opt_coords) {
- var mouseover =
- new goog.testing.events.Event(goog.events.EventType.MOUSEOVER, target);
- mouseover.relatedTarget = relatedTarget;
- goog.testing.events.setEventClientXY_(mouseover, opt_coords);
- return goog.testing.events.fireBrowserEvent(mouseover);
- };
- /**
- * Simulates a mousemove event on the given target.
- * @param {EventTarget} target The target for the event.
- * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
- * target's position (if available), otherwise (0, 0).
- * @return {boolean} The returnValue of the event: false if preventDefault() was
- * called on it, true otherwise.
- */
- goog.testing.events.fireMouseMoveEvent = function(target, opt_coords) {
- var mousemove =
- new goog.testing.events.Event(goog.events.EventType.MOUSEMOVE, target);
- goog.testing.events.setEventClientXY_(mousemove, opt_coords);
- return goog.testing.events.fireBrowserEvent(mousemove);
- };
- /**
- * Simulates a mouseout event on the given target.
- * @param {EventTarget} target The target for the event.
- * @param {EventTarget} relatedTarget The related target for the event (e.g.,
- * the node that the mouse is being moved into).
- * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
- * target's position (if available), otherwise (0, 0).
- * @return {boolean} The returnValue of the event: false if preventDefault() was
- * called on it, true otherwise.
- */
- goog.testing.events.fireMouseOutEvent = function(
- target, relatedTarget, opt_coords) {
- var mouseout =
- new goog.testing.events.Event(goog.events.EventType.MOUSEOUT, target);
- mouseout.relatedTarget = relatedTarget;
- goog.testing.events.setEventClientXY_(mouseout, opt_coords);
- return goog.testing.events.fireBrowserEvent(mouseout);
- };
- /**
- * Simulates a mousedown event on the given target.
- * @param {EventTarget} target The target for the event.
- * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
- * defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
- * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
- * target's position (if available), otherwise (0, 0).
- * @param {Object=} opt_eventProperties Event properties to be mixed into the
- * BrowserEvent.
- * @return {boolean} The returnValue of the event: false if preventDefault() was
- * called on it, true otherwise.
- */
- goog.testing.events.fireMouseDownEvent = function(
- target, opt_button, opt_coords, opt_eventProperties) {
- var button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT;
- button = !goog.events.BrowserFeature.HAS_W3C_BUTTON ?
- goog.events.BrowserEvent.IEButtonMap[button] :
- button;
- return goog.testing.events.fireMouseButtonEvent_(
- goog.events.EventType.MOUSEDOWN, target, button, opt_coords,
- opt_eventProperties);
- };
- /**
- * Simulates a mouseup event on the given target.
- * @param {EventTarget} target The target for the event.
- * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
- * defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
- * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
- * target's position (if available), otherwise (0, 0).
- * @param {Object=} opt_eventProperties Event properties to be mixed into the
- * BrowserEvent.
- * @return {boolean} The returnValue of the event: false if preventDefault() was
- * called on it, true otherwise.
- */
- goog.testing.events.fireMouseUpEvent = function(
- target, opt_button, opt_coords, opt_eventProperties) {
- var button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT;
- button = !goog.events.BrowserFeature.HAS_W3C_BUTTON ?
- goog.events.BrowserEvent.IEButtonMap[button] :
- button;
- return goog.testing.events.fireMouseButtonEvent_(
- goog.events.EventType.MOUSEUP, target, button, opt_coords,
- opt_eventProperties);
- };
- /**
- * Simulates a click event on the given target. IE only supports click with
- * the left mouse button.
- * @param {EventTarget} target The target for the event.
- * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
- * defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
- * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
- * target's position (if available), otherwise (0, 0).
- * @param {Object=} opt_eventProperties Event properties to be mixed into the
- * BrowserEvent.
- * @return {boolean} The returnValue of the event: false if preventDefault() was
- * called on it, true otherwise.
- */
- goog.testing.events.fireClickEvent = function(
- target, opt_button, opt_coords, opt_eventProperties) {
- return goog.testing.events.fireMouseButtonEvent_(
- goog.events.EventType.CLICK, target, opt_button, opt_coords,
- opt_eventProperties);
- };
- /**
- * Simulates a double-click event on the given target. Always double-clicks
- * with the left mouse button since no browser supports double-clicking with
- * any other buttons.
- * @param {EventTarget} target The target for the event.
- * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
- * target's position (if available), otherwise (0, 0).
- * @param {Object=} opt_eventProperties Event properties to be mixed into the
- * BrowserEvent.
- * @return {boolean} The returnValue of the event: false if preventDefault() was
- * called on it, true otherwise.
- */
- goog.testing.events.fireDoubleClickEvent = function(
- target, opt_coords, opt_eventProperties) {
- return goog.testing.events.fireMouseButtonEvent_(
- goog.events.EventType.DBLCLICK, target,
- goog.events.BrowserEvent.MouseButton.LEFT, opt_coords,
- opt_eventProperties);
- };
- /**
- * Helper function to fire a mouse event.
- * with the left mouse button since no browser supports double-clicking with
- * any other buttons.
- * @param {string} type The event type.
- * @param {EventTarget} target The target for the event.
- * @param {number=} opt_button Mouse button; defaults to
- * {@code goog.events.BrowserEvent.MouseButton.LEFT}.
- * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
- * target's position (if available), otherwise (0, 0).
- * @param {Object=} opt_eventProperties Event properties to be mixed into the
- * BrowserEvent.
- * @return {boolean} The returnValue of the event: false if preventDefault() was
- * called on it, true otherwise.
- * @private
- */
- goog.testing.events.fireMouseButtonEvent_ = function(
- type, target, opt_button, opt_coords, opt_eventProperties) {
- var e = new goog.testing.events.Event(type, target);
- e.button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT;
- goog.testing.events.setEventClientXY_(e, opt_coords);
- if (opt_eventProperties) {
- goog.object.extend(e, opt_eventProperties);
- }
- return goog.testing.events.fireBrowserEvent(e);
- };
- /**
- * Simulates a contextmenu event on the given target.
- * @param {EventTarget} target The target for the event.
- * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
- * target's position (if available), otherwise (0, 0).
- * @return {boolean} The returnValue of the event: false if preventDefault() was
- * called on it, true otherwise.
- */
- goog.testing.events.fireContextMenuEvent = function(target, opt_coords) {
- var button = (goog.userAgent.MAC && goog.userAgent.WEBKIT) ?
- goog.events.BrowserEvent.MouseButton.LEFT :
- goog.events.BrowserEvent.MouseButton.RIGHT;
- var contextmenu =
- new goog.testing.events.Event(goog.events.EventType.CONTEXTMENU, target);
- contextmenu.button = !goog.events.BrowserFeature.HAS_W3C_BUTTON ?
- goog.events.BrowserEvent.IEButtonMap[button] :
- button;
- contextmenu.ctrlKey = goog.userAgent.MAC;
- goog.testing.events.setEventClientXY_(contextmenu, opt_coords);
- return goog.testing.events.fireBrowserEvent(contextmenu);
- };
- /**
- * Simulates a mousedown, contextmenu, and the mouseup on the given event
- * target, with the right mouse button.
- * @param {EventTarget} target The target for the event.
- * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
- * target's position (if available), otherwise (0, 0).
- * @return {boolean} The returnValue of the sequence: false if preventDefault()
- * was called on any of the events, true otherwise.
- */
- goog.testing.events.fireContextMenuSequence = function(target, opt_coords) {
- var props = goog.userAgent.MAC ? {ctrlKey: true} : {};
- var button = (goog.userAgent.MAC && goog.userAgent.WEBKIT) ?
- goog.events.BrowserEvent.MouseButton.LEFT :
- goog.events.BrowserEvent.MouseButton.RIGHT;
- var result =
- goog.testing.events.fireMouseDownEvent(target, button, opt_coords, props);
- if (goog.userAgent.WINDOWS) {
- // All browsers are consistent on Windows.
- result &= goog.testing.events.fireMouseUpEvent(target, button, opt_coords) &
- goog.testing.events.fireContextMenuEvent(target, opt_coords);
- } else {
- result &= goog.testing.events.fireContextMenuEvent(target, opt_coords);
- // GECKO on Mac and Linux always fires the mouseup after the contextmenu.
- // WEBKIT is really weird.
- //
- // On Linux, it sometimes fires mouseup, but most of the time doesn't.
- // It's really hard to reproduce consistently. I think there's some
- // internal race condition. If contextmenu is preventDefaulted, then
- // mouseup always fires.
- //
- // On Mac, it always fires mouseup and then fires a click.
- result &=
- goog.testing.events.fireMouseUpEvent(target, button, opt_coords, props);
- if (goog.userAgent.WEBKIT && goog.userAgent.MAC) {
- result &=
- goog.testing.events.fireClickEvent(target, button, opt_coords, props);
- }
- }
- return !!result;
- };
- /**
- * Simulates a popstate event on the given target.
- * @param {EventTarget} target The target for the event.
- * @param {Object} state History state object.
- * @return {boolean} The returnValue of the event: false if preventDefault() was
- * called on it, true otherwise.
- */
- goog.testing.events.firePopStateEvent = function(target, state) {
- var e = new goog.testing.events.Event(goog.events.EventType.POPSTATE, target);
- e.state = state;
- return goog.testing.events.fireBrowserEvent(e);
- };
- /**
- * Simulate a blur event on the given target.
- * @param {EventTarget} target The target for the event.
- * @return {boolean} The value returned by firing the blur browser event,
- * which returns false iff 'preventDefault' was invoked.
- */
- goog.testing.events.fireBlurEvent = function(target) {
- var e = new goog.testing.events.Event(goog.events.EventType.BLUR, target);
- return goog.testing.events.fireBrowserEvent(e);
- };
- /**
- * Simulate a focus event on the given target.
- * @param {EventTarget} target The target for the event.
- * @return {boolean} The value returned by firing the focus browser event,
- * which returns false iff 'preventDefault' was invoked.
- */
- goog.testing.events.fireFocusEvent = function(target) {
- var e = new goog.testing.events.Event(goog.events.EventType.FOCUS, target);
- return goog.testing.events.fireBrowserEvent(e);
- };
- /**
- * Simulate a focus-in event on the given target.
- * @param {!EventTarget} target The target for the event.
- * @return {boolean} The value returned by firing the focus-in browser event,
- * which returns false iff 'preventDefault' was invoked.
- */
- goog.testing.events.fireFocusInEvent = function(target) {
- var e = new goog.testing.events.Event(goog.events.EventType.FOCUSIN, target);
- return goog.testing.events.fireBrowserEvent(e);
- };
- /**
- * Simulates an event's capturing and bubbling phases.
- * @param {Event} event A simulated native event. It will be wrapped in a
- * normalized BrowserEvent and dispatched to Closure listeners on all
- * ancestors of its target (inclusive).
- * @return {boolean} The returnValue of the event: false if preventDefault() was
- * called on it, true otherwise.
- */
- goog.testing.events.fireBrowserEvent = function(event) {
- event.returnValue_ = true;
- // generate a list of ancestors
- var ancestors = [];
- for (var current = event.target; current; current = current.parentNode) {
- ancestors.push(current);
- }
- // dispatch capturing listeners
- for (var j = ancestors.length - 1; j >= 0 && !event.propagationStopped_;
- j--) {
- goog.events.fireListeners(
- ancestors[j], event.type, true,
- new goog.events.BrowserEvent(event, ancestors[j]));
- }
- // dispatch bubbling listeners
- for (var j = 0; j < ancestors.length && !event.propagationStopped_; j++) {
- goog.events.fireListeners(
- ancestors[j], event.type, false,
- new goog.events.BrowserEvent(event, ancestors[j]));
- }
- return event.returnValue_;
- };
- /**
- * Simulates a touchstart event on the given target.
- * @param {EventTarget} target The target for the event.
- * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
- * target's position (if available), otherwise (0, 0).
- * @param {Object=} opt_eventProperties Event properties to be mixed into the
- * BrowserEvent.
- * @return {boolean} The returnValue of the event: false if preventDefault() was
- * called on it, true otherwise.
- */
- goog.testing.events.fireTouchStartEvent = function(
- target, opt_coords, opt_eventProperties) {
- // TODO: Support multi-touch events with array of coordinates.
- var touchstart =
- new goog.testing.events.Event(goog.events.EventType.TOUCHSTART, target);
- goog.testing.events.setEventClientXY_(touchstart, opt_coords);
- if (opt_eventProperties) {
- goog.object.extend(touchstart, opt_eventProperties);
- }
- return goog.testing.events.fireBrowserEvent(touchstart);
- };
- /**
- * Simulates a touchmove event on the given target.
- * @param {EventTarget} target The target for the event.
- * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
- * target's position (if available), otherwise (0, 0).
- * @param {Object=} opt_eventProperties Event properties to be mixed into the
- * BrowserEvent.
- * @return {boolean} The returnValue of the event: false if preventDefault() was
- * called on it, true otherwise.
- */
- goog.testing.events.fireTouchMoveEvent = function(
- target, opt_coords, opt_eventProperties) {
- // TODO: Support multi-touch events with array of coordinates.
- var touchmove =
- new goog.testing.events.Event(goog.events.EventType.TOUCHMOVE, target);
- goog.testing.events.setEventClientXY_(touchmove, opt_coords);
- if (opt_eventProperties) {
- goog.object.extend(touchmove, opt_eventProperties);
- }
- return goog.testing.events.fireBrowserEvent(touchmove);
- };
- /**
- * Simulates a touchend event on the given target.
- * @param {EventTarget} target The target for the event.
- * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
- * target's position (if available), otherwise (0, 0).
- * @param {Object=} opt_eventProperties Event properties to be mixed into the
- * BrowserEvent.
- * @return {boolean} The returnValue of the event: false if preventDefault() was
- * called on it, true otherwise.
- */
- goog.testing.events.fireTouchEndEvent = function(
- target, opt_coords, opt_eventProperties) {
- // TODO: Support multi-touch events with array of coordinates.
- var touchend =
- new goog.testing.events.Event(goog.events.EventType.TOUCHEND, target);
- goog.testing.events.setEventClientXY_(touchend, opt_coords);
- if (opt_eventProperties) {
- goog.object.extend(touchend, opt_eventProperties);
- }
- return goog.testing.events.fireBrowserEvent(touchend);
- };
- /**
- * Simulates a simple touch sequence on the given target.
- * @param {EventTarget} target The target for the event.
- * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event
- * target's position (if available), otherwise (0, 0).
- * @param {Object=} opt_eventProperties Event properties to be mixed into the
- * BrowserEvent.
- * @return {boolean} The returnValue of the sequence: false if preventDefault()
- * was called on any of the events, true otherwise.
- */
- goog.testing.events.fireTouchSequence = function(
- target, opt_coords, opt_eventProperties) {
- // TODO: Support multi-touch events with array of coordinates.
- // Fire touchstart, touchmove, touchend then return the bitwise AND of the 3.
- return !!(
- goog.testing.events.fireTouchStartEvent(
- target, opt_coords, opt_eventProperties) &
- goog.testing.events.fireTouchEndEvent(
- target, opt_coords, opt_eventProperties));
- };
- /**
- * Mixins a listenable into the given object. This turns the object
- * into a goog.events.Listenable. This is useful, for example, when
- * you need to mock a implementation of listenable and still want it
- * to work with goog.events.
- * @param {!Object} obj The object to mixin into.
- */
- goog.testing.events.mixinListenable = function(obj) {
- var listenable = new goog.events.EventTarget();
- listenable.setTargetForTesting(obj);
- var listenablePrototype = goog.events.EventTarget.prototype;
- var disposablePrototype = goog.Disposable.prototype;
- for (var key in listenablePrototype) {
- if (listenablePrototype.hasOwnProperty(key) ||
- disposablePrototype.hasOwnProperty(key)) {
- var member = listenablePrototype[key];
- if (goog.isFunction(member)) {
- obj[key] = goog.bind(member, listenable);
- } else {
- obj[key] = member;
- }
- }
- }
- };
|