410 lines
14 KiB
JavaScript
410 lines
14 KiB
JavaScript
// Copyright 2009 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.
|
|
|
|
goog.provide('goog.events.PasteHandlerTest');
|
|
goog.setTestOnly('goog.events.PasteHandlerTest');
|
|
|
|
goog.require('goog.dom');
|
|
goog.require('goog.events');
|
|
goog.require('goog.events.BrowserEvent');
|
|
goog.require('goog.events.EventTarget');
|
|
goog.require('goog.events.EventType');
|
|
goog.require('goog.events.KeyCodes');
|
|
goog.require('goog.events.PasteHandler');
|
|
goog.require('goog.testing.MockClock');
|
|
goog.require('goog.testing.MockUserAgent');
|
|
goog.require('goog.testing.jsunit');
|
|
goog.require('goog.userAgent');
|
|
|
|
function setUp() {
|
|
// TODO(user): fix {@code goog.testing.MockUserAgent} to do the right thing.
|
|
// the code doesn't seem to be updating the variables with
|
|
// goog.userAgent.init_(), which means it is not allowing me to mock the
|
|
// user agent variables.
|
|
goog.userAgent.GECKO = true;
|
|
goog.userAgent.IE = false;
|
|
goog.userAgent.WEBKIT = false;
|
|
goog.userAgent.VERSION = '1.8';
|
|
|
|
textarea = new goog.events.EventTarget();
|
|
textarea.value = '';
|
|
clock = new goog.testing.MockClock(true);
|
|
mockUserAgent = new goog.testing.MockUserAgent();
|
|
handler = new goog.events.PasteHandler(textarea);
|
|
pasted = false;
|
|
goog.events.listen(handler, goog.events.PasteHandler.EventType.PASTE,
|
|
function() {
|
|
pasted = true;
|
|
});
|
|
}
|
|
|
|
function tearDown() {
|
|
textarea.dispose();
|
|
handler.dispose();
|
|
clock.dispose();
|
|
mockUserAgent.dispose();
|
|
}
|
|
|
|
function newBrowserEvent(type) {
|
|
if (goog.isString(type)) {
|
|
return new goog.events.BrowserEvent({type: type});
|
|
} else {
|
|
return new goog.events.BrowserEvent(type);
|
|
}
|
|
}
|
|
|
|
function testDispatchingPasteEventSupportedByAFewBrowsersWork() {
|
|
goog.userAgent.IE = true;
|
|
var handlerThatSupportsPasteEvents =
|
|
new goog.events.PasteHandler(textarea);
|
|
// user clicks on the textarea and give it focus
|
|
goog.events.listen(handlerThatSupportsPasteEvents,
|
|
goog.events.PasteHandler.EventType.PASTE,
|
|
function() {
|
|
pasted = true;
|
|
});
|
|
textarea.dispatchEvent(newBrowserEvent('paste'));
|
|
assertTrue(pasted);
|
|
}
|
|
|
|
function testJustTypingDoesntFirePasteEvent() {
|
|
// user clicks on the textarea and give it focus
|
|
textarea.dispatchEvent(newBrowserEvent(goog.events.EventType.FOCUS));
|
|
assertFalse(pasted);
|
|
// user starts typing
|
|
textarea.dispatchEvent(newBrowserEvent({
|
|
type: goog.events.EventType.KEYDOWN,
|
|
keyCode: goog.events.KeyCodes.A
|
|
}));
|
|
textarea.value = 'a';
|
|
assertFalse(pasted);
|
|
|
|
// still typing
|
|
textarea.dispatchEvent({
|
|
type: goog.events.EventType.KEYDOWN,
|
|
keyCode: goog.events.KeyCodes.B
|
|
});
|
|
textarea.value = 'ab';
|
|
assertFalse(pasted);
|
|
|
|
// ends typing
|
|
textarea.dispatchEvent({
|
|
type: goog.events.EventType.KEYDOWN,
|
|
keyCode: goog.events.KeyCodes.C
|
|
});
|
|
textarea.value = 'abc';
|
|
assertFalse(pasted);
|
|
}
|
|
|
|
function testStartsOnInitialState() {
|
|
assertTrue(handler.getState() == goog.events.PasteHandler.State.INIT);
|
|
assertFalse(pasted);
|
|
}
|
|
|
|
function testBlurOnInit() {
|
|
textarea.dispatchEvent(goog.events.EventType.BLUR);
|
|
assertTrue(handler.getState() == goog.events.PasteHandler.State.INIT);
|
|
assertFalse(pasted);
|
|
}
|
|
|
|
function testFocusOnInit() {
|
|
textarea.dispatchEvent(goog.events.EventType.FOCUS);
|
|
assertTrue(handler.getState() == goog.events.PasteHandler.State.FOCUSED);
|
|
assertFalse(pasted);
|
|
}
|
|
|
|
function testInputOnFocus() {
|
|
// user clicks on the textarea
|
|
textarea.dispatchEvent(newBrowserEvent(goog.events.EventType.FOCUS));
|
|
clock.tick(
|
|
goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER +
|
|
1);
|
|
// and right click -> paste a text!
|
|
textarea.dispatchEvent(newBrowserEvent('input'));
|
|
assertTrue(handler.getState() == goog.events.PasteHandler.State.FOCUSED);
|
|
// make sure we detected it
|
|
assertTrue(pasted);
|
|
}
|
|
|
|
function testKeyPressOnFocus() {
|
|
// user clicks on the textarea
|
|
textarea.dispatchEvent(newBrowserEvent(goog.events.EventType.FOCUS));
|
|
|
|
// starts typing something
|
|
textarea.dispatchEvent(newBrowserEvent({
|
|
type: goog.events.EventType.KEYDOWN,
|
|
keyCode: goog.events.KeyCodes.A
|
|
}));
|
|
assertTrue(handler.getState() == goog.events.PasteHandler.State.TYPING);
|
|
assertFalse(pasted);
|
|
|
|
// and then presses ctrl+v
|
|
textarea.dispatchEvent(newBrowserEvent({
|
|
type: goog.events.EventType.KEYDOWN,
|
|
keyCode: goog.events.KeyCodes.V,
|
|
ctrlKey: true
|
|
}));
|
|
assertTrue(handler.getState() == goog.events.PasteHandler.State.TYPING);
|
|
|
|
// makes sure we detected it
|
|
assertTrue(pasted);
|
|
}
|
|
|
|
function testMouseOverOnInit() {
|
|
// user has something on the events
|
|
textarea.value = 'pasted string';
|
|
// and right click -> paste it on the textarea, WITHOUT giving focus
|
|
textarea.dispatchEvent(newBrowserEvent(goog.events.EventType.MOUSEOVER));
|
|
assertTrue(handler.getState() == goog.events.PasteHandler.State.INIT);
|
|
// makes sure we detect it
|
|
assertTrue(pasted);
|
|
|
|
pasted = false;
|
|
|
|
// user normaly mouseovers the textarea, with no text change
|
|
textarea.dispatchEvent(goog.events.EventType.MOUSEOVER);
|
|
assertTrue(handler.getState() == goog.events.PasteHandler.State.INIT);
|
|
// text area value doesnt change
|
|
assertFalse(pasted);
|
|
}
|
|
|
|
function testMouseOverAfterTyping() {
|
|
textarea.dispatchEvent(goog.events.EventType.FOCUS);
|
|
assertFalse(pasted);
|
|
textarea.dispatchEvent(
|
|
{type: goog.events.EventType.KEYDOWN, keyCode: goog.events.KeyCodes.A});
|
|
assertFalse(pasted);
|
|
textarea.value = 'a';
|
|
textarea.dispatchEvent('input');
|
|
assertFalse(pasted);
|
|
assertEquals('a', handler.oldValue_);
|
|
textarea.dispatchEvent(goog.events.EventType.MOUSEOVER);
|
|
assertFalse(pasted);
|
|
}
|
|
|
|
function testTypingAndThenRightClickPaste() {
|
|
textarea.dispatchEvent(goog.events.EventType.FOCUS);
|
|
|
|
textarea.dispatchEvent(
|
|
{type: goog.events.EventType.KEYDOWN, keyCode: goog.events.KeyCodes.A});
|
|
assertFalse(pasted);
|
|
textarea.value = 'a';
|
|
clock.tick(
|
|
goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER +
|
|
1);
|
|
textarea.dispatchEvent('input');
|
|
assertFalse(pasted);
|
|
|
|
assertEquals('a', handler.oldValue_);
|
|
|
|
textarea.value = 'ab';
|
|
clock.tick(
|
|
goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER +
|
|
1);
|
|
textarea.dispatchEvent(newBrowserEvent('input'));
|
|
assertTrue(pasted);
|
|
}
|
|
|
|
function testTypingReallyFastDispatchesTwoInputEventsBeforeTheKeyDownEvent() {
|
|
textarea.dispatchEvent(goog.events.EventType.FOCUS);
|
|
|
|
// keydown and input events seems to be fired indepently: even though input
|
|
// should happen after the key event, it doens't if the user types fast
|
|
// enough. FF2 + linux doesn't fire keydown events for every key pressed when
|
|
// you type fast enough. if one of the keydown events gets swallowed, two
|
|
// input events are fired consecutively. notice that there is a similar
|
|
// scenario, that actually does produce a valid paste action.
|
|
// {@see testRightClickRightClickAlsoDispatchesTwoConsecutiveInputEvents}
|
|
|
|
textarea.dispatchEvent(
|
|
{type: goog.events.EventType.KEYDOWN, keyCode: goog.events.KeyCodes.A});
|
|
assertFalse(pasted);
|
|
textarea.value = 'a';
|
|
clock.tick(
|
|
goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER -
|
|
1);
|
|
textarea.dispatchEvent('input');
|
|
assertFalse(pasted);
|
|
|
|
// second key down events gets fired on a different order
|
|
textarea.value = 'ab';
|
|
clock.tick(
|
|
goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER -
|
|
1);
|
|
textarea.dispatchEvent('input');
|
|
assertFalse(pasted);
|
|
}
|
|
|
|
function testRightClickRightClickAlsoDispatchesTwoConsecutiveInputEvents() {
|
|
textarea.dispatchEvent(goog.events.EventType.FOCUS);
|
|
|
|
// there is also another case that two consecutive INPUT events are fired,
|
|
// but in a valid paste action: if the user edit -> paste -> edit -> paste,
|
|
// it is a valid paste action.
|
|
|
|
textarea.value = 'a';
|
|
clock.tick(
|
|
goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER +
|
|
1);
|
|
textarea.dispatchEvent(newBrowserEvent('input'));
|
|
assertTrue(pasted);
|
|
|
|
// second key down events gets fired on a different order
|
|
textarea.value = 'ab';
|
|
clock.tick(
|
|
goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER +
|
|
1);
|
|
textarea.dispatchEvent(newBrowserEvent('input'));
|
|
assertTrue(pasted);
|
|
}
|
|
|
|
function testMiddleClickWithoutFocusTriggersPasteEvent() {
|
|
// if the textarea is NOT selected, and then we use the middle button,
|
|
// FF2+linux pastes what was last highlighted, causing a paste action.
|
|
textarea.dispatchEvent(goog.events.EventType.FOCUS);
|
|
textarea.dispatchEvent(newBrowserEvent('input'));
|
|
assertTrue(pasted);
|
|
}
|
|
|
|
|
|
function testMacRightClickPasteRequiresCtrlBecauseItHasOneButton() {
|
|
// Macs don't have two buttons mouse: this means that you need to press
|
|
// ctrl + click to get to the menu, and then you can click paste.
|
|
// The sequences of events fired on Opera are:
|
|
// focus -> keydown (keyCode == 0, not e.ctrlKey) -> input
|
|
goog.userAgent.OPERA = true;
|
|
goog.userAgent.MAC = true;
|
|
var handler = new goog.events.PasteHandler(textarea);
|
|
// user clicks on the textarea and give it focus
|
|
goog.events.listen(handler,
|
|
goog.events.PasteHandler.EventType.PASTE,
|
|
function() {
|
|
pasted = true;
|
|
});
|
|
textarea.dispatchEvent(goog.events.EventType.FOCUS);
|
|
assertFalse(pasted);
|
|
textarea.dispatchEvent({type: goog.events.EventType.KEYDOWN, keyCode: 0});
|
|
assertFalse(pasted);
|
|
clock.tick(
|
|
goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER +
|
|
1);
|
|
textarea.dispatchEvent(newBrowserEvent('input'));
|
|
assertTrue(pasted);
|
|
}
|
|
|
|
function testOperaMacFiresKeyCode17WhenAppleKeyPressedButDoesNotFireKeyDown() {
|
|
// Opera on Macs fires keycode 17 when apple key is pressed, and then it does
|
|
// not fire a keydown event when the V is pressed.
|
|
goog.userAgent.OPERA = true;
|
|
goog.userAgent.MAC = true;
|
|
var handler = new goog.events.PasteHandler(textarea);
|
|
// user clicks on the textarea and give it focus
|
|
goog.events.listen(handler,
|
|
goog.events.PasteHandler.EventType.PASTE,
|
|
function() {
|
|
pasted = true;
|
|
});
|
|
textarea.dispatchEvent(goog.events.EventType.FOCUS);
|
|
assertFalse(pasted);
|
|
// apple key is pressed, generating a keydown event
|
|
textarea.dispatchEvent({type: goog.events.EventType.KEYDOWN, keyCode: 17});
|
|
assertFalse(pasted);
|
|
clock.tick(
|
|
goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER +
|
|
1);
|
|
// and then text is added magically without any extra keydown events.
|
|
textarea.dispatchEvent(newBrowserEvent('input'));
|
|
assertTrue(pasted);
|
|
}
|
|
|
|
function testScriptingDoesntTriggerPasteEvents() {
|
|
var handlerUsedToListenForScriptingChanges =
|
|
new goog.events.PasteHandler(textarea);
|
|
pasted = false;
|
|
// user clicks on the textarea and give it focus
|
|
goog.events.listen(handlerUsedToListenForScriptingChanges,
|
|
goog.events.PasteHandler.EventType.PASTE,
|
|
function() {
|
|
pasted = true;
|
|
});
|
|
goog.dom.getElement('foo').value = 'dear paste handler,';
|
|
assertFalse(pasted);
|
|
goog.dom.getElement('foo').value = 'please dont misunderstand script changes';
|
|
assertFalse(pasted);
|
|
goog.dom.getElement('foo').value = 'with user generated paste events';
|
|
assertFalse(pasted);
|
|
goog.dom.getElement('foo').value = 'thanks!';
|
|
assertFalse(pasted);
|
|
}
|
|
|
|
function testAfterPaste() {
|
|
goog.userAgent.IE = true;
|
|
var handlerThatSupportsPasteEvents =
|
|
new goog.events.PasteHandler(textarea);
|
|
pasted = false;
|
|
goog.events.listen(handlerThatSupportsPasteEvents,
|
|
goog.events.PasteHandler.EventType.PASTE,
|
|
function() {
|
|
pasted = true;
|
|
});
|
|
var afterPasteFired = false;
|
|
goog.events.listen(handlerThatSupportsPasteEvents,
|
|
goog.events.PasteHandler.EventType.AFTER_PASTE,
|
|
function() {
|
|
afterPasteFired = true;
|
|
});
|
|
|
|
// Initial paste event comes before AFTER_PASTE has fired.
|
|
textarea.dispatchEvent(newBrowserEvent('paste'));
|
|
assertTrue(pasted);
|
|
assertFalse(afterPasteFired);
|
|
|
|
// Once text is pasted, it takes a bit to detect it, at which point
|
|
// AFTER_PASTE is fired.
|
|
clock.tick(goog.events.PasteHandler.PASTE_POLLING_PERIOD_MS_);
|
|
textarea.value = 'text';
|
|
clock.tick(goog.events.PasteHandler.PASTE_POLLING_PERIOD_MS_);
|
|
assertTrue(afterPasteFired);
|
|
}
|
|
|
|
|
|
function testAfterPasteNotFiredIfDelayTooLong() {
|
|
goog.userAgent.IE = true;
|
|
var handlerThatSupportsPasteEvents =
|
|
new goog.events.PasteHandler(textarea);
|
|
pasted = false;
|
|
goog.events.listen(handlerThatSupportsPasteEvents,
|
|
goog.events.PasteHandler.EventType.PASTE,
|
|
function() {
|
|
pasted = true;
|
|
});
|
|
var afterPasteFired = false;
|
|
goog.events.listen(handlerThatSupportsPasteEvents,
|
|
goog.events.PasteHandler.EventType.AFTER_PASTE,
|
|
function() {
|
|
afterPasteFired = true;
|
|
});
|
|
|
|
// Initial paste event comes before AFTER_PASTE has fired.
|
|
textarea.dispatchEvent(newBrowserEvent('paste'));
|
|
assertTrue(pasted);
|
|
assertFalse(afterPasteFired);
|
|
|
|
// If the new text doesn't show up in time, we never fire AFTER_PASTE.
|
|
clock.tick(goog.events.PasteHandler.PASTE_POLLING_TIMEOUT_MS_);
|
|
textarea.value = 'text';
|
|
clock.tick(goog.events.PasteHandler.PASTE_POLLING_PERIOD_MS_);
|
|
assertFalse(afterPasteFired);
|
|
}
|