Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/testing/events/events.js
4523 views
1
/**
2
* @license
3
* Copyright The Closure Library Authors.
4
* SPDX-License-Identifier: Apache-2.0
5
*/
6
7
/**
8
* @fileoverview Event Simulation.
9
*
10
* Utility functions for simulating events at the Closure level. All functions
11
* in this package generate events by calling goog.events.fireListeners,
12
* rather than interfacing with the browser directly. This is intended for
13
* testing purposes, and should not be used in production code.
14
*
15
* The decision to use Closure events and dispatchers instead of the browser's
16
* native events and dispatchers was conscious and deliberate. Native event
17
* dispatchers have their own set of quirks and edge cases. Pure JS dispatchers
18
* are more robust and transparent.
19
*
20
* If you think you need a testing mechanism that uses native Event objects,
21
* please, please email closure-tech first to explain your use case before you
22
* sink time into this.
23
*
24
* TODO(user): Migrate to explicitly non-nullable types. At present, many
25
* functions in this file expect non-null inputs but do not explicitly
26
* indicate this.
27
*/
28
29
goog.setTestOnly('goog.testing.events');
30
goog.provide('goog.testing.events');
31
goog.provide('goog.testing.events.Event');
32
33
goog.require('goog.Disposable');
34
goog.require('goog.asserts');
35
goog.require('goog.dom.NodeType');
36
goog.require('goog.events');
37
goog.require('goog.events.BrowserEvent');
38
goog.require('goog.events.EventTarget');
39
goog.require('goog.events.EventType');
40
goog.require('goog.events.KeyCodes');
41
goog.require('goog.object');
42
goog.require('goog.style');
43
goog.require('goog.userAgent');
44
goog.requireType('goog.math.Coordinate');
45
46
47
48
/**
49
* goog.events.BrowserEvent expects an Event so we provide one for JSCompiler.
50
*
51
* This clones a lot of the functionality of goog.events.Event. This used to
52
* use a mixin, but the mixin results in confusing the two types when compiled.
53
*
54
* @param {string} type Event Type.
55
* @param {Object=} opt_target Reference to the object that is the target of
56
* this event.
57
* @constructor
58
* @extends {Event}
59
*/
60
goog.testing.events.Event = function(type, opt_target) {
61
'use strict';
62
this.type = type;
63
64
this.target = /** @type {EventTarget} */ (opt_target || null);
65
66
this.currentTarget = this.target;
67
};
68
69
70
/**
71
* Whether to cancel the event in internal capture/bubble processing for IE.
72
* @type {boolean}
73
* @public
74
* @suppress {underscore|visibility} Technically public, but referencing this
75
* outside this package is strongly discouraged.
76
*/
77
goog.testing.events.Event.prototype.propagationStopped_ = false;
78
79
80
/** @override */
81
goog.testing.events.Event.prototype.defaultPrevented = false;
82
83
84
/**
85
* Return value for in internal capture/bubble processing for IE.
86
* @type {boolean}
87
* @public
88
* @suppress {underscore|visibility} Technically public, but referencing this
89
* outside this package is strongly discouraged.
90
*/
91
goog.testing.events.Event.prototype.returnValue_ = true;
92
93
94
/** @override */
95
goog.testing.events.Event.prototype.stopPropagation = function() {
96
'use strict';
97
this.propagationStopped_ = true;
98
};
99
100
101
/** @override */
102
goog.testing.events.Event.prototype.preventDefault = function() {
103
'use strict';
104
this.defaultPrevented = true;
105
this.returnValue_ = false;
106
};
107
108
/**
109
* Asserts an event target exists. This will fail if target is not defined.
110
*
111
* TODO(nnaze): Gradually add this to the methods in this file, and eventually
112
* update the method signatures to not take nullables. See
113
* http://b/8961907
114
*
115
* @param {EventTarget} target A target to assert.
116
* @return {!EventTarget} The target, guaranteed to exist.
117
* @private
118
*/
119
goog.testing.events.assertEventTarget_ = function(target) {
120
'use strict';
121
return goog.asserts.assert(target, 'EventTarget should be defined.');
122
};
123
124
125
/**
126
* A static helper function that sets the mouse position to the event.
127
* @param {Event} event A simulated native event.
128
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
129
* target's position (if available), otherwise (0, 0).
130
* @private
131
*/
132
goog.testing.events.setEventClientXY_ = function(event, opt_coords) {
133
'use strict';
134
if (!opt_coords && event.target &&
135
/** @type {!Node} */ (event.target).nodeType ==
136
goog.dom.NodeType.ELEMENT) {
137
try {
138
opt_coords = goog.style.getClientPosition(
139
/** @type {!Element} **/ (event.target));
140
} catch (ex) {
141
// IE sometimes throws if it can't get the position.
142
}
143
}
144
event.clientX = opt_coords ? opt_coords.x : 0;
145
event.clientY = opt_coords ? opt_coords.y : 0;
146
147
// Pretend the browser window is at (0, 0) of the screen.
148
event.screenX = event.clientX;
149
event.screenY = event.clientY;
150
151
// Assume that there was no page scroll.
152
event.pageX = event.clientX;
153
event.pageY = event.clientY;
154
};
155
156
157
/**
158
* Simulates a mousedown, mouseup, and then click on the given event target,
159
* with the left mouse button.
160
* @param {EventTarget} target The target for the event.
161
* @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
162
* defaults to `goog.events.BrowserEvent.MouseButton.LEFT`.
163
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
164
* target's position (if available), otherwise (0, 0).
165
* @param {Object=} opt_eventProperties Event properties to be mixed into the
166
* BrowserEvent.
167
* @return {boolean} The returnValue of the sequence: false if preventDefault()
168
* was called on any of the events, true otherwise.
169
*/
170
goog.testing.events.fireClickSequence = function(
171
target, opt_button, opt_coords, opt_eventProperties) {
172
'use strict';
173
// Fire mousedown, mouseup, and click. Then return the bitwise AND of the 3.
174
return goog.testing.events.eagerAnd_(
175
goog.testing.events.fireMouseDownEvent(
176
target, opt_button, opt_coords, opt_eventProperties),
177
goog.testing.events.fireMouseUpEvent(
178
target, opt_button, opt_coords, opt_eventProperties),
179
goog.testing.events.fireClickEvent(
180
target, opt_button, opt_coords, opt_eventProperties));
181
};
182
183
184
/**
185
* Simulates the sequence of events fired by the browser when the user double-
186
* clicks the given target.
187
* @param {EventTarget} target The target for the event.
188
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
189
* target's position (if available), otherwise (0, 0).
190
* @param {Object=} opt_eventProperties Event properties to be mixed into the
191
* BrowserEvent.
192
* @return {boolean} The returnValue of the sequence: false if preventDefault()
193
* was called on any of the events, true otherwise.
194
*/
195
goog.testing.events.fireDoubleClickSequence = function(
196
target, opt_coords, opt_eventProperties) {
197
'use strict';
198
// Fire mousedown, mouseup, click, mousedown, mouseup, click, dblclick.
199
// Then return the bitwise AND of the 7.
200
const btn = goog.events.BrowserEvent.MouseButton.LEFT;
201
return goog.testing.events.eagerAnd_(
202
goog.testing.events.fireMouseDownEvent(
203
target, btn, opt_coords, opt_eventProperties),
204
goog.testing.events.fireMouseUpEvent(
205
target, btn, opt_coords, opt_eventProperties),
206
goog.testing.events.fireClickEvent(
207
target, btn, opt_coords, opt_eventProperties),
208
// IE fires a selectstart instead of the second mousedown in a
209
// dblclick, but we don't care about selectstart.
210
(goog.userAgent.IE ||
211
goog.testing.events.fireMouseDownEvent(
212
target, btn, opt_coords, opt_eventProperties)),
213
goog.testing.events.fireMouseUpEvent(
214
target, btn, opt_coords, opt_eventProperties),
215
// IE doesn't fire the second click in a dblclick.
216
(goog.userAgent.IE ||
217
goog.testing.events.fireClickEvent(
218
target, btn, opt_coords, opt_eventProperties)),
219
goog.testing.events.fireDoubleClickEvent(
220
target, opt_coords, opt_eventProperties));
221
};
222
223
224
/**
225
* A non-exhaustive mapping of keys to keyCode. These are not localized and
226
* are specific to QWERTY keyboards, but are used to augment our testing key
227
* events as much as possible in order to simulate real browser events. This
228
* will be used to fill out the `keyCode` field for key events when the `key`
229
* value is present in this map.
230
* @private {!Object<number>}
231
* @final
232
*/
233
goog.testing.events.KEY_TO_KEYCODE_MAPPING_ = {
234
'0': goog.events.KeyCodes.ZERO,
235
'1': goog.events.KeyCodes.ONE,
236
'2': goog.events.KeyCodes.TWO,
237
'3': goog.events.KeyCodes.THREE,
238
'4': goog.events.KeyCodes.FOUR,
239
'5': goog.events.KeyCodes.FIVE,
240
'6': goog.events.KeyCodes.SIX,
241
'7': goog.events.KeyCodes.SEVEN,
242
'8': goog.events.KeyCodes.EIGHT,
243
'9': goog.events.KeyCodes.NINE,
244
'a': goog.events.KeyCodes.A,
245
'b': goog.events.KeyCodes.B,
246
'c': goog.events.KeyCodes.C,
247
'd': goog.events.KeyCodes.D,
248
'e': goog.events.KeyCodes.E,
249
'f': goog.events.KeyCodes.F,
250
'g': goog.events.KeyCodes.G,
251
'h': goog.events.KeyCodes.H,
252
'i': goog.events.KeyCodes.I,
253
'j': goog.events.KeyCodes.J,
254
'k': goog.events.KeyCodes.K,
255
'l': goog.events.KeyCodes.L,
256
'm': goog.events.KeyCodes.M,
257
'n': goog.events.KeyCodes.N,
258
'o': goog.events.KeyCodes.O,
259
'p': goog.events.KeyCodes.P,
260
'q': goog.events.KeyCodes.Q,
261
'r': goog.events.KeyCodes.R,
262
's': goog.events.KeyCodes.S,
263
't': goog.events.KeyCodes.T,
264
'u': goog.events.KeyCodes.U,
265
'v': goog.events.KeyCodes.V,
266
'w': goog.events.KeyCodes.W,
267
'x': goog.events.KeyCodes.X,
268
'y': goog.events.KeyCodes.Y,
269
'z': goog.events.KeyCodes.Z
270
};
271
272
273
/**
274
* Simulates a complete keystroke (keydown, keypress, and keyup). Note that
275
* if preventDefault is called on the keydown, the keypress will not fire.
276
*
277
* @param {EventTarget} target The target for the event.
278
* @param {string|number} keyOrKeyCode The key value or keycode of the key
279
* pressed.
280
* @param {Object=} opt_eventProperties Event properties to be mixed into the
281
* BrowserEvent.
282
* @return {boolean} The returnValue of the sequence: false if preventDefault()
283
* was called on any of the events, true otherwise.
284
*/
285
goog.testing.events.fireKeySequence = function(
286
target, keyOrKeyCode, opt_eventProperties) {
287
'use strict';
288
return goog.testing.events.fireNonAsciiKeySequence(
289
target, keyOrKeyCode, keyOrKeyCode, opt_eventProperties);
290
};
291
292
293
/**
294
* Simulates a complete keystroke (keydown, keypress, and keyup) when typing
295
* a non-ASCII character. Same as fireKeySequence, the keypress will not fire
296
* if preventDefault is called on the keydown.
297
*
298
* @param {EventTarget} target The target for the event.
299
* @param {string|number} keyOrKeyCode The key value or keycode of the keydown
300
* and keyup events.
301
* @param {string|number} keyPressKeyOrKeyCode The key value or keycode of the
302
* keypress event.
303
* @param {Object=} opt_eventProperties Event properties to be mixed into the
304
* BrowserEvent.
305
* @return {boolean} The returnValue of the sequence: false if preventDefault()
306
* was called on any of the events, true otherwise.
307
*/
308
goog.testing.events.fireNonAsciiKeySequence = function(
309
target, keyOrKeyCode, keyPressKeyOrKeyCode, opt_eventProperties) {
310
'use strict';
311
const keydown =
312
/** @type {!KeyboardEvent} */ (
313
/** @type {!Event} */ (new goog.testing.events.Event(
314
goog.events.EventType.KEYDOWN, target)));
315
const keyup = //
316
/** @type {!KeyboardEvent} */ (
317
/** @type {!Event} */ (new goog.testing.events.Event(
318
goog.events.EventType.KEYUP, target)));
319
const keypress =
320
/** @type {!KeyboardEvent} */ (
321
/** @type {!Event} */ (new goog.testing.events.Event(
322
goog.events.EventType.KEYPRESS, target)));
323
324
if (typeof keyOrKeyCode === 'string') {
325
keydown.key = keyup.key = /** @type {string} */ (keyOrKeyCode);
326
keypress.key = /** @type {string} */ (keyPressKeyOrKeyCode);
327
328
// Try to fill the keyCode field for the key events if we have a known key.
329
// This is to try and make these mock simulated event as close to real
330
// browser events as possible.
331
const mappedKeyCode =
332
goog.testing.events
333
.KEY_TO_KEYCODE_MAPPING_[/** @type {string} */ (keyOrKeyCode)
334
.toLowerCase()];
335
if (mappedKeyCode) {
336
keydown.keyCode = keyup.keyCode = mappedKeyCode;
337
}
338
339
const mappedKeyPressKeyCode =
340
goog.testing.events.KEY_TO_KEYCODE_MAPPING_[/** @type {string} */ (
341
keyPressKeyOrKeyCode)
342
.toLowerCase()];
343
if (mappedKeyPressKeyCode) {
344
keypress.keyCode = mappedKeyPressKeyCode;
345
}
346
} else {
347
keydown.keyCode = keyup.keyCode = /** @type {number} */ (keyOrKeyCode);
348
keypress.keyCode = /** @type {number} */ (keyPressKeyOrKeyCode);
349
}
350
351
if (opt_eventProperties) {
352
goog.object.extend(keydown, opt_eventProperties);
353
goog.object.extend(keyup, opt_eventProperties);
354
goog.object.extend(keypress, opt_eventProperties);
355
}
356
357
// Fire keydown, keypress, and keyup. Note that if the keydown is
358
// prevent-defaulted, then the keypress will not fire.
359
let result = goog.testing.events.fireBrowserEvent(keydown);
360
if (typeof keyOrKeyCode === 'string') {
361
if (/** @type {string} */ (keyPressKeyOrKeyCode) != '' && result) {
362
result = goog.testing.events.eagerAnd_(
363
result, goog.testing.events.fireBrowserEvent(keypress));
364
}
365
} else {
366
if (goog.events.KeyCodes.firesKeyPressEvent(
367
/** @type {number} */ (keyOrKeyCode), undefined, keydown.shiftKey,
368
keydown.ctrlKey, keydown.altKey, keydown.metaKey) &&
369
result) {
370
result = goog.testing.events.eagerAnd_(
371
result, goog.testing.events.fireBrowserEvent(keypress));
372
}
373
}
374
return goog.testing.events.eagerAnd_(
375
result, goog.testing.events.fireBrowserEvent(keyup));
376
};
377
378
379
/**
380
* Simulates a mouseenter event on the given target.
381
* @param {!EventTarget} target The target for the event.
382
* @param {?EventTarget} relatedTarget The related target for the event (e.g.,
383
* the node that the mouse is being moved out of).
384
* @param {!goog.math.Coordinate=} opt_coords Mouse position. Defaults to
385
* event's target's position (if available), otherwise (0, 0).
386
* @return {boolean} The returnValue of the event: false if preventDefault() was
387
* called on it, true otherwise.
388
*/
389
goog.testing.events.fireMouseEnterEvent = function(
390
target, relatedTarget, opt_coords) {
391
'use strict';
392
const mouseenter =
393
new goog.testing.events.Event(goog.events.EventType.MOUSEENTER, target);
394
mouseenter.relatedTarget = relatedTarget;
395
goog.testing.events.setEventClientXY_(mouseenter, opt_coords);
396
return goog.testing.events.fireBrowserEvent(mouseenter);
397
};
398
399
400
/**
401
* Simulates a mouseleave event on the given target.
402
* @param {!EventTarget} target The target for the event.
403
* @param {?EventTarget} relatedTarget The related target for the event (e.g.,
404
* the node that the mouse is being moved into).
405
* @param {!goog.math.Coordinate=} opt_coords Mouse position. Defaults to
406
* event's target's position (if available), otherwise (0, 0).
407
* @return {boolean} The returnValue of the event: false if preventDefault() was
408
* called on it, true otherwise.
409
*/
410
goog.testing.events.fireMouseLeaveEvent = function(
411
target, relatedTarget, opt_coords) {
412
'use strict';
413
const mouseleave =
414
new goog.testing.events.Event(goog.events.EventType.MOUSELEAVE, target);
415
mouseleave.relatedTarget = relatedTarget;
416
goog.testing.events.setEventClientXY_(mouseleave, opt_coords);
417
return goog.testing.events.fireBrowserEvent(mouseleave);
418
};
419
420
421
/**
422
* Simulates a mouseover event on the given target.
423
* @param {EventTarget} target The target for the event.
424
* @param {EventTarget} relatedTarget The related target for the event (e.g.,
425
* the node that the mouse is being moved out of).
426
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
427
* target's position (if available), otherwise (0, 0).
428
* @return {boolean} The returnValue of the event: false if preventDefault() was
429
* called on it, true otherwise.
430
*/
431
goog.testing.events.fireMouseOverEvent = function(
432
target, relatedTarget, opt_coords) {
433
'use strict';
434
const mouseover =
435
new goog.testing.events.Event(goog.events.EventType.MOUSEOVER, target);
436
mouseover.relatedTarget = relatedTarget;
437
goog.testing.events.setEventClientXY_(mouseover, opt_coords);
438
return goog.testing.events.fireBrowserEvent(mouseover);
439
};
440
441
442
/**
443
* Simulates a mousemove event on the given target.
444
* @param {EventTarget} target The target for the event.
445
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
446
* target's position (if available), otherwise (0, 0).
447
* @return {boolean} The returnValue of the event: false if preventDefault() was
448
* called on it, true otherwise.
449
*/
450
goog.testing.events.fireMouseMoveEvent = function(target, opt_coords) {
451
'use strict';
452
const mousemove =
453
new goog.testing.events.Event(goog.events.EventType.MOUSEMOVE, target);
454
455
goog.testing.events.setEventClientXY_(mousemove, opt_coords);
456
return goog.testing.events.fireBrowserEvent(mousemove);
457
};
458
459
460
/**
461
* Simulates a mouseout event on the given target.
462
* @param {EventTarget} target The target for the event.
463
* @param {EventTarget} relatedTarget The related target for the event (e.g.,
464
* the node that the mouse is being moved into).
465
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
466
* target's position (if available), otherwise (0, 0).
467
* @return {boolean} The returnValue of the event: false if preventDefault() was
468
* called on it, true otherwise.
469
*/
470
goog.testing.events.fireMouseOutEvent = function(
471
target, relatedTarget, opt_coords) {
472
'use strict';
473
const mouseout =
474
new goog.testing.events.Event(goog.events.EventType.MOUSEOUT, target);
475
mouseout.relatedTarget = relatedTarget;
476
goog.testing.events.setEventClientXY_(mouseout, opt_coords);
477
return goog.testing.events.fireBrowserEvent(mouseout);
478
};
479
480
481
/**
482
* Simulates a mousedown event on the given target.
483
* @param {EventTarget} target The target for the event.
484
* @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
485
* defaults to `goog.events.BrowserEvent.MouseButton.LEFT`.
486
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
487
* target's position (if available), otherwise (0, 0).
488
* @param {Object=} opt_eventProperties Event properties to be mixed into the
489
* BrowserEvent.
490
* @return {boolean} The returnValue of the event: false if preventDefault() was
491
* called on it, true otherwise.
492
*/
493
goog.testing.events.fireMouseDownEvent = function(
494
target, opt_button, opt_coords, opt_eventProperties) {
495
'use strict';
496
let button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT;
497
return goog.testing.events.fireMouseButtonEvent_(
498
goog.events.EventType.MOUSEDOWN, target, button, opt_coords,
499
opt_eventProperties);
500
};
501
502
503
/**
504
* Simulates a mouseup event on the given target.
505
* @param {EventTarget} target The target for the event.
506
* @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
507
* defaults to `goog.events.BrowserEvent.MouseButton.LEFT`.
508
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
509
* target's position (if available), otherwise (0, 0).
510
* @param {Object=} opt_eventProperties Event properties to be mixed into the
511
* BrowserEvent.
512
* @return {boolean} The returnValue of the event: false if preventDefault() was
513
* called on it, true otherwise.
514
*/
515
goog.testing.events.fireMouseUpEvent = function(
516
target, opt_button, opt_coords, opt_eventProperties) {
517
'use strict';
518
let button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT;
519
return goog.testing.events.fireMouseButtonEvent_(
520
goog.events.EventType.MOUSEUP, target, button, opt_coords,
521
opt_eventProperties);
522
};
523
524
525
/**
526
* Simulates a click event on the given target. IE only supports click with
527
* the left mouse button.
528
* @param {EventTarget} target The target for the event.
529
* @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
530
* defaults to `goog.events.BrowserEvent.MouseButton.LEFT`.
531
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
532
* target's position (if available), otherwise (0, 0).
533
* @param {Object=} opt_eventProperties Event properties to be mixed into the
534
* BrowserEvent.
535
* @return {boolean} The returnValue of the event: false if preventDefault() was
536
* called on it, true otherwise.
537
*/
538
goog.testing.events.fireClickEvent = function(
539
target, opt_button, opt_coords, opt_eventProperties) {
540
'use strict';
541
return goog.testing.events.fireMouseButtonEvent_(
542
goog.events.EventType.CLICK, target, opt_button, opt_coords,
543
opt_eventProperties);
544
};
545
546
547
/**
548
* Simulates a double-click event on the given target. Always double-clicks
549
* with the left mouse button since no browser supports double-clicking with
550
* any other buttons.
551
* @param {EventTarget} target The target for the event.
552
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
553
* target's position (if available), otherwise (0, 0).
554
* @param {Object=} opt_eventProperties Event properties to be mixed into the
555
* BrowserEvent.
556
* @return {boolean} The returnValue of the event: false if preventDefault() was
557
* called on it, true otherwise.
558
*/
559
goog.testing.events.fireDoubleClickEvent = function(
560
target, opt_coords, opt_eventProperties) {
561
'use strict';
562
return goog.testing.events.fireMouseButtonEvent_(
563
goog.events.EventType.DBLCLICK, target,
564
goog.events.BrowserEvent.MouseButton.LEFT, opt_coords,
565
opt_eventProperties);
566
};
567
568
569
/**
570
* Helper function to fire a mouse event.
571
* with the left mouse button since no browser supports double-clicking with
572
* any other buttons.
573
* @param {string} type The event type.
574
* @param {EventTarget} target The target for the event.
575
* @param {number=} opt_button Mouse button; defaults to
576
* `goog.events.BrowserEvent.MouseButton.LEFT`.
577
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
578
* target's position (if available), otherwise (0, 0).
579
* @param {Object=} opt_eventProperties Event properties to be mixed into the
580
* BrowserEvent.
581
* @return {boolean} The returnValue of the event: false if preventDefault() was
582
* called on it, true otherwise.
583
* @private
584
*/
585
goog.testing.events.fireMouseButtonEvent_ = function(
586
type, target, opt_button, opt_coords, opt_eventProperties) {
587
'use strict';
588
const e = new goog.testing.events.Event(type, target);
589
e.button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT;
590
goog.testing.events.setEventClientXY_(e, opt_coords);
591
if (opt_eventProperties) {
592
goog.object.extend(e, opt_eventProperties);
593
}
594
return goog.testing.events.fireBrowserEvent(e);
595
};
596
597
598
/**
599
* Simulates a contextmenu event on the given target.
600
* @param {EventTarget} target The target for the event.
601
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
602
* target's position (if available), otherwise (0, 0).
603
* @return {boolean} The returnValue of the event: false if preventDefault() was
604
* called on it, true otherwise.
605
*/
606
goog.testing.events.fireContextMenuEvent = function(target, opt_coords) {
607
'use strict';
608
const button = (goog.userAgent.MAC && goog.userAgent.WEBKIT) ?
609
goog.events.BrowserEvent.MouseButton.LEFT :
610
goog.events.BrowserEvent.MouseButton.RIGHT;
611
const contextmenu =
612
new goog.testing.events.Event(goog.events.EventType.CONTEXTMENU, target);
613
contextmenu.button = button;
614
contextmenu.ctrlKey = goog.userAgent.MAC;
615
goog.testing.events.setEventClientXY_(contextmenu, opt_coords);
616
return goog.testing.events.fireBrowserEvent(contextmenu);
617
};
618
619
620
/**
621
* Simulates a mousedown, contextmenu, and the mouseup on the given event
622
* target, with the right mouse button.
623
* @param {EventTarget} target The target for the event.
624
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
625
* target's position (if available), otherwise (0, 0).
626
* @return {boolean} The returnValue of the sequence: false if preventDefault()
627
* was called on any of the events, true otherwise.
628
*/
629
goog.testing.events.fireContextMenuSequence = function(target, opt_coords) {
630
'use strict';
631
const props = goog.userAgent.MAC ? {ctrlKey: true} : {};
632
const button = (goog.userAgent.MAC && goog.userAgent.WEBKIT) ?
633
goog.events.BrowserEvent.MouseButton.LEFT :
634
goog.events.BrowserEvent.MouseButton.RIGHT;
635
636
let result =
637
goog.testing.events.fireMouseDownEvent(target, button, opt_coords, props);
638
if (goog.userAgent.WINDOWS) {
639
// All browsers are consistent on Windows.
640
result = goog.testing.events.eagerAnd_(
641
result,
642
goog.testing.events.fireMouseUpEvent(target, button, opt_coords),
643
goog.testing.events.fireContextMenuEvent(target, opt_coords));
644
} else {
645
result = goog.testing.events.eagerAnd_(
646
result, goog.testing.events.fireContextMenuEvent(target, opt_coords));
647
648
// GECKO on Mac and Linux always fires the mouseup after the contextmenu.
649
650
// WEBKIT is really weird.
651
//
652
// On Linux, it sometimes fires mouseup, but most of the time doesn't.
653
// It's really hard to reproduce consistently. I think there's some
654
// internal race condition. If contextmenu is preventDefaulted, then
655
// mouseup always fires.
656
//
657
// On Mac, it always fires mouseup and then fires a click.
658
result = goog.testing.events.eagerAnd_(
659
result,
660
goog.testing.events.fireMouseUpEvent(
661
target, button, opt_coords, props));
662
663
if (goog.userAgent.WEBKIT && goog.userAgent.MAC) {
664
result = goog.testing.events.eagerAnd_(
665
result,
666
goog.testing.events.fireClickEvent(
667
target, button, opt_coords, props));
668
}
669
}
670
return result;
671
};
672
673
674
/**
675
* Simulates a popstate event on the given target.
676
* @param {EventTarget} target The target for the event.
677
* @param {Object} state History state object.
678
* @return {boolean} The returnValue of the event: false if preventDefault() was
679
* called on it, true otherwise.
680
*/
681
goog.testing.events.firePopStateEvent = function(target, state) {
682
'use strict';
683
const e = /** @type {!PopStateEvent} */ (/** @type {!Event} */ (
684
new goog.testing.events.Event(goog.events.EventType.POPSTATE, target)));
685
e.state = state;
686
return goog.testing.events.fireBrowserEvent(e);
687
};
688
689
690
/**
691
* Simulate a blur event on the given target.
692
* @param {EventTarget} target The target for the event.
693
* @return {boolean} The value returned by firing the blur browser event,
694
* which returns false iff 'preventDefault' was invoked.
695
*/
696
goog.testing.events.fireBlurEvent = function(target) {
697
'use strict';
698
const e = new goog.testing.events.Event(goog.events.EventType.BLUR, target);
699
return goog.testing.events.fireBrowserEvent(e);
700
};
701
702
703
/**
704
* Simulate a focus event on the given target.
705
* @param {EventTarget} target The target for the event.
706
* @return {boolean} The value returned by firing the focus browser event,
707
* which returns false iff 'preventDefault' was invoked.
708
*/
709
goog.testing.events.fireFocusEvent = function(target) {
710
'use strict';
711
const e = new goog.testing.events.Event(goog.events.EventType.FOCUS, target);
712
return goog.testing.events.fireBrowserEvent(e);
713
};
714
715
716
/**
717
* Simulate a focus-in event on the given target.
718
* @param {!EventTarget} target The target for the event.
719
* @return {boolean} The value returned by firing the focus-in browser event,
720
* which returns false iff 'preventDefault' was invoked.
721
*/
722
goog.testing.events.fireFocusInEvent = function(target) {
723
'use strict';
724
const e =
725
new goog.testing.events.Event(goog.events.EventType.FOCUSIN, target);
726
return goog.testing.events.fireBrowserEvent(e);
727
};
728
729
730
/**
731
* Simulates an event's capturing and bubbling phases.
732
* @param {Event} event A simulated native event. It will be wrapped in a
733
* normalized BrowserEvent and dispatched to Closure listeners on all
734
* ancestors of its target (inclusive).
735
* @return {boolean} The returnValue of the event: false if preventDefault() was
736
* called on it, true otherwise.
737
*/
738
goog.testing.events.fireBrowserEvent = function(event) {
739
'use strict';
740
event = /** @type {!goog.testing.events.Event} */ (event);
741
742
event.returnValue_ = true;
743
744
// generate a list of ancestors
745
const ancestors = [];
746
for (let current = event.target; current; current = current.parentNode) {
747
ancestors.push(current);
748
}
749
750
// dispatch capturing listeners
751
for (let j = ancestors.length - 1; j >= 0 && !event.propagationStopped_;
752
j--) {
753
goog.events.fireListeners(
754
ancestors[j], event.type, true,
755
new goog.events.BrowserEvent(event, ancestors[j]));
756
}
757
758
// dispatch bubbling listeners
759
for (let j = 0; j < ancestors.length && !event.propagationStopped_; j++) {
760
goog.events.fireListeners(
761
ancestors[j], event.type, false,
762
new goog.events.BrowserEvent(event, ancestors[j]));
763
}
764
765
return event.returnValue_;
766
};
767
768
769
/**
770
* Simulates a touchstart event on the given target.
771
* @param {EventTarget} target The target for the event.
772
* @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
773
* target's position (if available), otherwise (0, 0).
774
* @param {Object=} opt_eventProperties Event properties to be mixed into the
775
* BrowserEvent.
776
* @return {boolean} The returnValue of the event: false if preventDefault() was
777
* called on it, true otherwise.
778
*/
779
goog.testing.events.fireTouchStartEvent = function(
780
target, opt_coords, opt_eventProperties) {
781
'use strict';
782
// TODO: Support multi-touch events with array of coordinates.
783
const touchstart =
784
new goog.testing.events.Event(goog.events.EventType.TOUCHSTART, target);
785
goog.testing.events.setEventClientXY_(touchstart, opt_coords);
786
if (opt_eventProperties) {
787
goog.object.extend(touchstart, opt_eventProperties);
788
}
789
return goog.testing.events.fireBrowserEvent(touchstart);
790
};
791
792
793
/**
794
* Simulates a touchmove event on the given target.
795
* @param {EventTarget} target The target for the event.
796
* @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
797
* target's position (if available), otherwise (0, 0).
798
* @param {Object=} opt_eventProperties Event properties to be mixed into the
799
* BrowserEvent.
800
* @return {boolean} The returnValue of the event: false if preventDefault() was
801
* called on it, true otherwise.
802
*/
803
goog.testing.events.fireTouchMoveEvent = function(
804
target, opt_coords, opt_eventProperties) {
805
'use strict';
806
// TODO: Support multi-touch events with array of coordinates.
807
const touchmove =
808
new goog.testing.events.Event(goog.events.EventType.TOUCHMOVE, target);
809
goog.testing.events.setEventClientXY_(touchmove, opt_coords);
810
if (opt_eventProperties) {
811
goog.object.extend(touchmove, opt_eventProperties);
812
}
813
return goog.testing.events.fireBrowserEvent(touchmove);
814
};
815
816
817
/**
818
* Simulates a touchend event on the given target.
819
* @param {EventTarget} target The target for the event.
820
* @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
821
* target's position (if available), otherwise (0, 0).
822
* @param {Object=} opt_eventProperties Event properties to be mixed into the
823
* BrowserEvent.
824
* @return {boolean} The returnValue of the event: false if preventDefault() was
825
* called on it, true otherwise.
826
*/
827
goog.testing.events.fireTouchEndEvent = function(
828
target, opt_coords, opt_eventProperties) {
829
'use strict';
830
// TODO: Support multi-touch events with array of coordinates.
831
const touchend =
832
new goog.testing.events.Event(goog.events.EventType.TOUCHEND, target);
833
goog.testing.events.setEventClientXY_(touchend, opt_coords);
834
if (opt_eventProperties) {
835
goog.object.extend(touchend, opt_eventProperties);
836
}
837
return goog.testing.events.fireBrowserEvent(touchend);
838
};
839
840
841
/**
842
* Simulates a simple touch sequence on the given target.
843
* @param {EventTarget} target The target for the event.
844
* @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event
845
* target's position (if available), otherwise (0, 0).
846
* @param {Object=} opt_eventProperties Event properties to be mixed into the
847
* BrowserEvent.
848
* @return {boolean} The returnValue of the sequence: false if preventDefault()
849
* was called on any of the events, true otherwise.
850
*/
851
goog.testing.events.fireTouchSequence = function(
852
target, opt_coords, opt_eventProperties) {
853
'use strict';
854
// TODO: Support multi-touch events with array of coordinates.
855
// Fire touchstart, touchmove, touchend then return the AND of the 2.
856
return goog.testing.events.eagerAnd_(
857
goog.testing.events.fireTouchStartEvent(
858
target, opt_coords, opt_eventProperties),
859
goog.testing.events.fireTouchEndEvent(
860
target, opt_coords, opt_eventProperties));
861
};
862
863
864
/**
865
* Mixins a listenable into the given object. This turns the object
866
* into a goog.events.Listenable. This is useful, for example, when
867
* you need to mock a implementation of listenable and still want it
868
* to work with goog.events.
869
* @param {!Object} obj The object to mixin into.
870
*/
871
goog.testing.events.mixinListenable = function(obj) {
872
'use strict';
873
const listenable = new goog.events.EventTarget();
874
875
listenable.setTargetForTesting(obj);
876
877
const listenablePrototype = goog.events.EventTarget.prototype;
878
const disposablePrototype = goog.Disposable.prototype;
879
for (let key in listenablePrototype) {
880
if (listenablePrototype.hasOwnProperty(key) ||
881
disposablePrototype.hasOwnProperty(key)) {
882
const member = listenablePrototype[key];
883
if (typeof member === 'function') {
884
obj[key] = goog.bind(member, listenable);
885
} else {
886
obj[key] = member;
887
}
888
}
889
}
890
};
891
892
/**
893
* Returns the boolean AND of all parameters.
894
*
895
* Unlike directly using `&&`, using this function cannot employ
896
* short-circuiting; all side effects of resolving parameters will occur before
897
* entering the function body.
898
*
899
* @param {boolean} first
900
* @param {...boolean} rest
901
* @return {boolean}
902
* @private
903
*/
904
goog.testing.events.eagerAnd_ = function(first, rest) {
905
'use strict';
906
for (let i = 1; i < arguments.length; i++) {
907
first = first && arguments[i];
908
}
909
return first;
910
};
911
912