blob: 7072f1fac337dd4b5d1e8f0bb15b4c3cfbbd5beb [file] [log] [blame]
// 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);
}