1279 lines
42 KiB
JavaScript
1279 lines
42 KiB
JavaScript
// Copyright 2012 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 Shared tests for goog.editor.Field and ContentEditableField.
|
|
* Since ContentEditableField switches many of the internal code paths in Field
|
|
* (such as via usesIframe()) it's important to re-run a lot of the same tests.
|
|
*
|
|
* @author nicksantos@google.com (Nick Santos)
|
|
* @author jparent@google.com (Julie Parent)
|
|
* @author gboyer@google.com (Garrett Boyer)
|
|
*/
|
|
|
|
/** @suppress {extraProvide} */
|
|
goog.provide('goog.editor.field_test');
|
|
|
|
goog.require('goog.dom');
|
|
goog.require('goog.dom.Range');
|
|
goog.require('goog.editor.BrowserFeature');
|
|
goog.require('goog.editor.Field');
|
|
goog.require('goog.editor.Plugin');
|
|
goog.require('goog.editor.range');
|
|
goog.require('goog.events');
|
|
goog.require('goog.events.BrowserEvent');
|
|
goog.require('goog.events.KeyCodes');
|
|
goog.require('goog.functions');
|
|
goog.require('goog.testing.LooseMock');
|
|
goog.require('goog.testing.MockClock');
|
|
goog.require('goog.testing.dom');
|
|
goog.require('goog.testing.events');
|
|
goog.require('goog.testing.events.Event');
|
|
goog.require('goog.testing.recordFunction');
|
|
goog.require('goog.userAgent');
|
|
goog.setTestOnly('Tests for goog.editor.*Field');
|
|
|
|
|
|
/** Constructor to use for creating the field. Set by the test HTML file. */
|
|
var FieldConstructor;
|
|
|
|
|
|
/** Hard-coded HTML for the tests. */
|
|
var HTML = '<div id="testField">I am text.</div>';
|
|
|
|
|
|
function setUp() {
|
|
goog.dom.getElement('parent').innerHTML = HTML;
|
|
assertTrue('FieldConstructor should be set by the test HTML file',
|
|
goog.isFunction(FieldConstructor));
|
|
}
|
|
|
|
|
|
function tearDown() {
|
|
// NOTE(nicksantos): I think IE is blowing up on this call because
|
|
// it is lame. It manifests its lameness by throwing an exception.
|
|
// Kudos to XT for helping me to figure this out.
|
|
try {
|
|
goog.events.removeAllNativeListeners();
|
|
} catch (e) {}
|
|
}
|
|
|
|
// Tests for the plugin interface.
|
|
|
|
|
|
|
|
/**
|
|
* Dummy plugin for test usage.
|
|
* @constructor
|
|
* @extends {goog.editor.Plugin}
|
|
*/
|
|
function TestPlugin() {
|
|
this.getTrogClassId = function() {
|
|
return 'TestPlugin';
|
|
};
|
|
|
|
this.handleKeyDown = goog.nullFunction;
|
|
this.handleKeyPress = goog.nullFunction;
|
|
this.handleKeyUp = goog.nullFunction;
|
|
this.handleKeyboardShortcut = goog.nullFunction;
|
|
this.isSupportedCommand = goog.nullFunction;
|
|
this.execCommandInternal = goog.nullFunction;
|
|
this.queryCommandValue = goog.nullFunction;
|
|
this.activeOnUneditableFields = goog.nullFunction;
|
|
this.handleSelectionChange = goog.nullFunction;
|
|
}
|
|
goog.inherits(TestPlugin, goog.editor.Plugin);
|
|
|
|
|
|
/**
|
|
* Tests that calling registerPlugin will add the plugin to the
|
|
* plugin map.
|
|
*/
|
|
function testRegisterPlugin() {
|
|
var editableField = new FieldConstructor('testField');
|
|
var plugin = new TestPlugin();
|
|
|
|
editableField.registerPlugin(plugin);
|
|
|
|
assertEquals('Registered plugin must be in protected plugin map.',
|
|
plugin, editableField.plugins_[plugin.getTrogClassId()]);
|
|
assertEquals('Plugin has a keydown handler, should be in keydown map',
|
|
plugin,
|
|
editableField.indexedPlugins_[goog.editor.Plugin.Op.KEYDOWN][0]);
|
|
assertEquals('Plugin has a keypress handler, should be in keypress map',
|
|
plugin,
|
|
editableField.indexedPlugins_[goog.editor.Plugin.Op.KEYPRESS][0]);
|
|
assertEquals('Plugin has a keyup handler, should be in keuup map',
|
|
plugin,
|
|
editableField.indexedPlugins_[goog.editor.Plugin.Op.KEYUP][0]);
|
|
assertEquals(
|
|
'Plugin has a selectionchange handler, should be in selectionchange map',
|
|
plugin,
|
|
editableField.indexedPlugins_[goog.editor.Plugin.Op.SELECTION][0]);
|
|
assertEquals('Plugin has a shortcut handler, should be in shortcut map',
|
|
plugin,
|
|
editableField.indexedPlugins_[goog.editor.Plugin.Op.SHORTCUT][0]);
|
|
assertEquals('Plugin has a execCommand, should be in execCommand map',
|
|
plugin,
|
|
editableField.indexedPlugins_[goog.editor.Plugin.Op.EXEC_COMMAND][0]);
|
|
assertEquals('Plugin has a queryCommand, should be in queryCommand map',
|
|
plugin,
|
|
editableField.indexedPlugins_[goog.editor.Plugin.Op.QUERY_COMMAND][0]);
|
|
assertEquals('Plugin does not have a prepareContentsHtml,' +
|
|
'should not be in prepareContentsHtml map',
|
|
undefined,
|
|
editableField.indexedPlugins_[
|
|
goog.editor.Plugin.Op.PREPARE_CONTENTS_HTML][0]);
|
|
assertEquals('Plugin does not have a cleanContentsDom,' +
|
|
'should not be in cleanContentsDom map',
|
|
undefined,
|
|
editableField.indexedPlugins_[
|
|
goog.editor.Plugin.Op.CLEAN_CONTENTS_DOM][0]);
|
|
assertEquals('Plugin does not have a cleanContentsHtml,' +
|
|
'should not be in cleanContentsHtml map',
|
|
undefined,
|
|
editableField.indexedPlugins_[
|
|
goog.editor.Plugin.Op.CLEAN_CONTENTS_HTML][0]);
|
|
|
|
editableField.dispose();
|
|
}
|
|
|
|
|
|
/**
|
|
* Tests that calling unregisterPlugin will remove the plugin from
|
|
* the map.
|
|
*/
|
|
function testUnregisterPlugin() {
|
|
var editableField = new FieldConstructor('testField');
|
|
var plugin = new TestPlugin();
|
|
|
|
editableField.registerPlugin(plugin);
|
|
editableField.unregisterPlugin(plugin);
|
|
|
|
assertUndefined('Unregistered plugin must not be in protected plugin map.',
|
|
editableField.plugins_[plugin.getTrogClassId()]);
|
|
|
|
editableField.dispose();
|
|
}
|
|
|
|
|
|
/**
|
|
* Tests that registered plugins can be fetched by their id.
|
|
*/
|
|
function testGetPluginByClassId() {
|
|
var editableField = new FieldConstructor('testField');
|
|
var plugin = new TestPlugin();
|
|
|
|
assertUndefined('Must not be able to get unregistered plugins by class id.',
|
|
editableField.getPluginByClassId(plugin.getTrogClassId()));
|
|
|
|
editableField.registerPlugin(plugin);
|
|
assertEquals('Must be able to get registered plugins by class id.',
|
|
plugin, editableField.getPluginByClassId(plugin.getTrogClassId()));
|
|
editableField.dispose();
|
|
}
|
|
|
|
|
|
/**
|
|
* Tests that plugins get auto disposed by default when the field is disposed.
|
|
* Tests that plugins with setAutoDispose(false) do not get disposed when the
|
|
* field is disposed.
|
|
*/
|
|
function testDisposed_PluginAutoDispose() {
|
|
var editableField = new FieldConstructor('testField');
|
|
var plugin = new TestPlugin();
|
|
|
|
var noDisposePlugin = new goog.editor.Plugin();
|
|
noDisposePlugin.getTrogClassId = function() {
|
|
return 'noDisposeId';
|
|
};
|
|
noDisposePlugin.setAutoDispose(false);
|
|
|
|
editableField.registerPlugin(plugin);
|
|
editableField.registerPlugin(noDisposePlugin);
|
|
editableField.dispose();
|
|
assert(editableField.isDisposed());
|
|
assertTrue(plugin.isDisposed());
|
|
assertFalse(noDisposePlugin.isDisposed());
|
|
}
|
|
|
|
var STRING_KEY = String.fromCharCode(goog.events.KeyCodes.A).toLowerCase();
|
|
|
|
|
|
/**
|
|
* @return {goog.events.Event} Returns an event for a keyboard shortcut
|
|
* for the letter 'a'.
|
|
*/
|
|
function getBrowserEvent() {
|
|
var e = new goog.events.BrowserEvent();
|
|
e.ctrlKey = true;
|
|
e.metaKey = true;
|
|
e.charCode = goog.events.KeyCodes.A;
|
|
return e;
|
|
}
|
|
|
|
|
|
/**
|
|
* Tests that plugins are disabled when the field is made uneditable.
|
|
*/
|
|
|
|
function testMakeUneditableDisablesPlugins() {
|
|
var editableField = new FieldConstructor('testField');
|
|
var plugin = new TestPlugin();
|
|
|
|
var calls = 0;
|
|
plugin.disable = function(field) {
|
|
assertEquals(editableField, field);
|
|
assertTrue(field.isUneditable());
|
|
calls++;
|
|
};
|
|
|
|
editableField.registerPlugin(plugin);
|
|
editableField.makeEditable();
|
|
|
|
assertEquals(0, calls);
|
|
editableField.makeUneditable();
|
|
|
|
assertEquals(1, calls);
|
|
|
|
editableField.dispose();
|
|
}
|
|
|
|
|
|
/**
|
|
* Test that if a plugin registers keyup, it gets called.
|
|
*/
|
|
function testPluginKeyUp() {
|
|
var editableField = new FieldConstructor('testField');
|
|
var plugin = new TestPlugin();
|
|
var e = getBrowserEvent();
|
|
|
|
var mockPlugin = new goog.testing.LooseMock(plugin);
|
|
mockPlugin.getTrogClassId().$returns('mockPlugin');
|
|
mockPlugin.registerFieldObject(editableField);
|
|
mockPlugin.isEnabled(editableField).$anyTimes().$returns(true);
|
|
mockPlugin.handleKeyUp(e);
|
|
mockPlugin.$replay();
|
|
|
|
editableField.registerPlugin(mockPlugin);
|
|
|
|
editableField.handleKeyUp_(e);
|
|
|
|
mockPlugin.$verify();
|
|
}
|
|
|
|
|
|
/**
|
|
* Test that if a plugin registers keydown, it gets called.
|
|
*/
|
|
function testPluginKeyDown() {
|
|
var editableField = new FieldConstructor('testField');
|
|
var plugin = new TestPlugin();
|
|
var e = getBrowserEvent();
|
|
|
|
var mockPlugin = new goog.testing.LooseMock(plugin);
|
|
mockPlugin.getTrogClassId().$returns('mockPlugin');
|
|
mockPlugin.registerFieldObject(editableField);
|
|
mockPlugin.isEnabled(editableField).$anyTimes().$returns(true);
|
|
mockPlugin.handleKeyDown(e).$returns(true);
|
|
mockPlugin.$replay();
|
|
|
|
editableField.registerPlugin(mockPlugin);
|
|
|
|
editableField.handleKeyDown_(e);
|
|
|
|
mockPlugin.$verify();
|
|
}
|
|
|
|
|
|
/**
|
|
* Test that if a plugin registers keypress, it gets called.
|
|
*/
|
|
function testPluginKeyPress() {
|
|
var editableField = new FieldConstructor('testField');
|
|
var plugin = new TestPlugin();
|
|
var e = getBrowserEvent();
|
|
|
|
var mockPlugin = new goog.testing.LooseMock(plugin);
|
|
mockPlugin.getTrogClassId().$returns('mockPlugin');
|
|
mockPlugin.registerFieldObject(editableField);
|
|
mockPlugin.isEnabled(editableField).$anyTimes().$returns(true);
|
|
mockPlugin.handleKeyPress(e).$returns(true);
|
|
mockPlugin.$replay();
|
|
|
|
editableField.registerPlugin(mockPlugin);
|
|
|
|
editableField.handleKeyPress_(e);
|
|
|
|
mockPlugin.$verify();
|
|
}
|
|
|
|
|
|
/**
|
|
* If one plugin handles a key event, the rest of the plugins do not get their
|
|
* key handlers invoked.
|
|
*/
|
|
function testHandledKeyEvent() {
|
|
var editableField = new FieldConstructor('testField');
|
|
var plugin = new TestPlugin();
|
|
var e = getBrowserEvent();
|
|
|
|
var mockPlugin1 = new goog.testing.LooseMock(plugin);
|
|
mockPlugin1.getTrogClassId().$returns('mockPlugin1');
|
|
mockPlugin1.registerFieldObject(editableField);
|
|
mockPlugin1.isEnabled(editableField).$anyTimes().$returns(true);
|
|
if (goog.editor.BrowserFeature.USES_KEYDOWN) {
|
|
mockPlugin1.handleKeyDown(e).$returns(true);
|
|
} else {
|
|
mockPlugin1.handleKeyPress(e).$returns(true);
|
|
}
|
|
mockPlugin1.handleKeyUp(e).$returns(true);
|
|
mockPlugin1.$replay();
|
|
|
|
var mockPlugin2 = new goog.testing.LooseMock(plugin);
|
|
mockPlugin2.getTrogClassId().$returns('mockPlugin2');
|
|
mockPlugin2.registerFieldObject(editableField);
|
|
mockPlugin2.isEnabled(editableField).$anyTimes().$returns(true);
|
|
mockPlugin2.$replay();
|
|
|
|
editableField.registerPlugin(mockPlugin1);
|
|
editableField.registerPlugin(mockPlugin2);
|
|
|
|
editableField.handleKeyUp_(e);
|
|
if (goog.editor.BrowserFeature.USES_KEYDOWN) {
|
|
editableField.handleKeyDown_(e);
|
|
} else {
|
|
editableField.handleKeyPress_(e);
|
|
}
|
|
|
|
mockPlugin1.$verify();
|
|
mockPlugin2.$verify();
|
|
}
|
|
|
|
|
|
/**
|
|
* Tests to make sure the cut and paste events are not dispatched immediately.
|
|
*/
|
|
function testHandleCutAndPasteEvents() {
|
|
if (goog.editor.BrowserFeature.USE_MUTATION_EVENTS) {
|
|
// Cut and paste events do not raise events at all in Mozilla.
|
|
return;
|
|
}
|
|
var editableField = new FieldConstructor('testField');
|
|
var clock = new goog.testing.MockClock(true);
|
|
var delayedChanges = goog.testing.recordFunction();
|
|
goog.events.listen(editableField, goog.editor.Field.EventType.DELAYEDCHANGE,
|
|
delayedChanges);
|
|
|
|
editableField.makeEditable();
|
|
|
|
goog.testing.events.fireBrowserEvent(
|
|
new goog.testing.events.Event('cut', editableField.getElement()));
|
|
assertEquals('Cut event should be on a timer', 0,
|
|
delayedChanges.getCallCount());
|
|
clock.tick(1000);
|
|
assertEquals('delayed change event should fire within 1s after cut', 1,
|
|
delayedChanges.getCallCount());
|
|
|
|
goog.testing.events.fireBrowserEvent(
|
|
new goog.testing.events.Event('paste', editableField.getElement()));
|
|
assertEquals('Paste event should be on a timer', 1,
|
|
delayedChanges.getCallCount());
|
|
clock.tick(1000);
|
|
assertEquals('delayed change event should fire within 1s after paste', 2,
|
|
delayedChanges.getCallCount());
|
|
|
|
clock.dispose();
|
|
editableField.dispose();
|
|
}
|
|
|
|
|
|
/**
|
|
* If the first plugin does not handle the key event, the next plugin gets
|
|
* a chance to handle it.
|
|
*/
|
|
function testNotHandledKeyEvent() {
|
|
var editableField = new FieldConstructor('testField');
|
|
var plugin = new TestPlugin();
|
|
var e = getBrowserEvent();
|
|
|
|
var mockPlugin1 = new goog.testing.LooseMock(plugin);
|
|
mockPlugin1.getTrogClassId().$returns('mockPlugin1');
|
|
mockPlugin1.registerFieldObject(editableField);
|
|
mockPlugin1.isEnabled(editableField).$anyTimes().$returns(true);
|
|
if (goog.editor.BrowserFeature.USES_KEYDOWN) {
|
|
mockPlugin1.handleKeyDown(e).$returns(false);
|
|
} else {
|
|
mockPlugin1.handleKeyPress(e).$returns(false);
|
|
}
|
|
mockPlugin1.handleKeyUp(e).$returns(false);
|
|
mockPlugin1.$replay();
|
|
|
|
var mockPlugin2 = new goog.testing.LooseMock(plugin);
|
|
mockPlugin2.getTrogClassId().$returns('mockPlugin2');
|
|
mockPlugin2.registerFieldObject(editableField);
|
|
mockPlugin2.isEnabled(editableField).$anyTimes().$returns(true);
|
|
if (goog.editor.BrowserFeature.USES_KEYDOWN) {
|
|
mockPlugin2.handleKeyDown(e).$returns(true);
|
|
} else {
|
|
mockPlugin2.handleKeyPress(e).$returns(true);
|
|
}
|
|
mockPlugin2.handleKeyUp(e).$returns(true);
|
|
mockPlugin2.$replay();
|
|
|
|
editableField.registerPlugin(mockPlugin1);
|
|
editableField.registerPlugin(mockPlugin2);
|
|
|
|
editableField.handleKeyUp_(e);
|
|
if (goog.editor.BrowserFeature.USES_KEYDOWN) {
|
|
editableField.handleKeyDown_(e);
|
|
} else {
|
|
editableField.handleKeyPress_(e);
|
|
}
|
|
|
|
mockPlugin1.$verify();
|
|
mockPlugin2.$verify();
|
|
}
|
|
|
|
|
|
/**
|
|
* Make sure that handleKeyboardShortcut is called if other key handlers
|
|
* return false.
|
|
*/
|
|
function testKeyboardShortcutCalled() {
|
|
var editableField = new FieldConstructor('testField');
|
|
var plugin = new TestPlugin();
|
|
var e = getBrowserEvent();
|
|
|
|
var mockPlugin = new goog.testing.LooseMock(plugin);
|
|
mockPlugin.getTrogClassId().$returns('mockPlugin');
|
|
mockPlugin.registerFieldObject(editableField);
|
|
mockPlugin.isEnabled(editableField).$anyTimes().$returns(true);
|
|
if (goog.editor.BrowserFeature.USES_KEYDOWN) {
|
|
mockPlugin.handleKeyDown(e).$returns(false);
|
|
} else {
|
|
mockPlugin.handleKeyPress(e).$returns(false);
|
|
}
|
|
mockPlugin.handleKeyboardShortcut(e, STRING_KEY, true).$returns(false);
|
|
mockPlugin.$replay();
|
|
|
|
editableField.registerPlugin(mockPlugin);
|
|
|
|
if (goog.editor.BrowserFeature.USES_KEYDOWN) {
|
|
editableField.handleKeyDown_(e);
|
|
} else {
|
|
editableField.handleKeyPress_(e);
|
|
}
|
|
|
|
mockPlugin.$verify();
|
|
}
|
|
|
|
|
|
/**
|
|
* Make sure that handleKeyboardShortcut is not called if other key handlers
|
|
* return true.
|
|
*/
|
|
function testKeyboardShortcutNotCalled() {
|
|
var editableField = new FieldConstructor('testField');
|
|
var plugin = new TestPlugin();
|
|
var e = getBrowserEvent();
|
|
|
|
var mockPlugin = new goog.testing.LooseMock(plugin);
|
|
mockPlugin.getTrogClassId().$returns('mockPlugin');
|
|
mockPlugin.registerFieldObject(editableField);
|
|
mockPlugin.isEnabled(editableField).$anyTimes().$returns(true);
|
|
if (goog.editor.BrowserFeature.USES_KEYDOWN) {
|
|
mockPlugin.handleKeyDown(e).$returns(true);
|
|
} else {
|
|
mockPlugin.handleKeyPress(e).$returns(true);
|
|
}
|
|
mockPlugin.$replay();
|
|
|
|
editableField.registerPlugin(mockPlugin);
|
|
|
|
if (goog.editor.BrowserFeature.USES_KEYDOWN) {
|
|
editableField.handleKeyDown_(e);
|
|
} else {
|
|
editableField.handleKeyPress_(e);
|
|
}
|
|
|
|
mockPlugin.$verify();
|
|
}
|
|
|
|
|
|
/**
|
|
* Make sure that handleKeyboardShortcut is not called if alt is pressed.
|
|
* @bug 1363959
|
|
*/
|
|
function testKeyHandlingAlt() {
|
|
var editableField = new FieldConstructor('testField');
|
|
var plugin = new TestPlugin();
|
|
var e = getBrowserEvent();
|
|
e.altKey = true;
|
|
|
|
var mockPlugin = new goog.testing.LooseMock(plugin);
|
|
mockPlugin.getTrogClassId().$returns('mockPlugin');
|
|
mockPlugin.registerFieldObject(editableField);
|
|
mockPlugin.isEnabled(editableField).$anyTimes().$returns(true);
|
|
if (goog.editor.BrowserFeature.USES_KEYDOWN) {
|
|
mockPlugin.handleKeyDown(e).$returns(false);
|
|
} else {
|
|
mockPlugin.handleKeyPress(e).$returns(false);
|
|
}
|
|
mockPlugin.$replay();
|
|
|
|
editableField.registerPlugin(mockPlugin);
|
|
|
|
if (goog.editor.BrowserFeature.USES_KEYDOWN) {
|
|
editableField.handleKeyDown_(e);
|
|
} else {
|
|
editableField.handleKeyPress_(e);
|
|
}
|
|
|
|
mockPlugin.$verify();
|
|
}
|
|
|
|
|
|
/**
|
|
* Test that if a plugin has an execCommand function, it gets called
|
|
* but only for supported commands.
|
|
*/
|
|
function testPluginExecCommand() {
|
|
var plugin = new TestPlugin();
|
|
var passedCommand, passedArg;
|
|
plugin.execCommand = function(command, arg) {
|
|
passedCommand = command;
|
|
passedArg = arg;
|
|
};
|
|
|
|
var editableField = new FieldConstructor('testField');
|
|
editableField.registerPlugin(plugin);
|
|
plugin.enable(editableField);
|
|
plugin.isSupportedCommand = goog.functions.constant(true);
|
|
|
|
editableField.execCommand('+indent', true);
|
|
// Verify that the plugin's execCommand was called with the correct
|
|
// args.
|
|
assertEquals('+indent', passedCommand);
|
|
assertTrue(passedArg);
|
|
|
|
passedCommand = null;
|
|
passedArg = null;
|
|
plugin.isSupportedCommand = goog.functions.constant(false);
|
|
|
|
editableField.execCommand('+outdent', false);
|
|
// Verify that a plugin's execCommand is not called if it isn't a supported
|
|
// command.
|
|
assertNull(passedCommand);
|
|
assertNull(passedArg);
|
|
|
|
editableField.dispose();
|
|
plugin.dispose();
|
|
}
|
|
|
|
|
|
/**
|
|
* Test that if one plugin supports execCommand, no other plugins
|
|
* get a chance to handle the execComand.
|
|
*/
|
|
function testSupportedExecCommand() {
|
|
var editableField = new FieldConstructor('testField');
|
|
var plugin = new TestPlugin();
|
|
|
|
var mockPlugin1 = new goog.testing.LooseMock(plugin);
|
|
mockPlugin1.getTrogClassId().$returns('mockPlugin1');
|
|
mockPlugin1.registerFieldObject(editableField);
|
|
mockPlugin1.isEnabled(editableField).$anyTimes().$returns(true);
|
|
mockPlugin1.isSupportedCommand('+indent').$returns(true);
|
|
mockPlugin1.execCommandInternal('+indent').$returns(true);
|
|
mockPlugin1.execCommand('+indent').$does(
|
|
function() { mockPlugin1.execCommandInternal('+indent'); });
|
|
mockPlugin1.$replay();
|
|
|
|
var mockPlugin2 = new goog.testing.LooseMock(plugin);
|
|
mockPlugin2.getTrogClassId().$returns('mockPlugin2');
|
|
mockPlugin2.registerFieldObject(editableField);
|
|
mockPlugin2.isEnabled(editableField).$anyTimes().$returns(true);
|
|
mockPlugin2.$replay();
|
|
|
|
editableField.registerPlugin(mockPlugin1);
|
|
editableField.registerPlugin(mockPlugin2);
|
|
|
|
editableField.execCommand('+indent');
|
|
|
|
mockPlugin1.$verify();
|
|
mockPlugin2.$verify();
|
|
}
|
|
|
|
|
|
/**
|
|
* Test that if the first plugin does not support execCommand, the other
|
|
* plugins get a chance to handle the execCommand.
|
|
*/
|
|
function testNotSupportedExecCommand() {
|
|
var editableField = new FieldConstructor('testField');
|
|
var plugin = new TestPlugin();
|
|
|
|
var mockPlugin1 = new goog.testing.LooseMock(plugin);
|
|
mockPlugin1.getTrogClassId().$returns('mockPlugin1');
|
|
mockPlugin1.registerFieldObject(editableField);
|
|
mockPlugin1.isEnabled(editableField).$anyTimes().$returns(true);
|
|
mockPlugin1.isSupportedCommand('+indent').$returns(false);
|
|
mockPlugin1.$replay();
|
|
|
|
var mockPlugin2 = new goog.testing.LooseMock(plugin);
|
|
mockPlugin2.getTrogClassId().$returns('mockPlugin2');
|
|
mockPlugin2.registerFieldObject(editableField);
|
|
mockPlugin2.isEnabled(editableField).$anyTimes().$returns(true);
|
|
mockPlugin2.isSupportedCommand('+indent').$returns(true);
|
|
mockPlugin2.execCommandInternal('+indent').$returns(true);
|
|
mockPlugin2.execCommand('+indent').$does(
|
|
function() { mockPlugin2.execCommandInternal('+indent'); });
|
|
mockPlugin2.$replay();
|
|
|
|
editableField.registerPlugin(mockPlugin1);
|
|
editableField.registerPlugin(mockPlugin2);
|
|
|
|
editableField.execCommand('+indent');
|
|
|
|
mockPlugin1.$verify();
|
|
mockPlugin2.$verify();
|
|
}
|
|
|
|
|
|
/**
|
|
* Tests that if a plugin supports a command that its queryCommandValue
|
|
* gets called and no further plugins can handle the queryCommandValue.
|
|
*/
|
|
function testSupportedQueryCommand() {
|
|
var editableField = new FieldConstructor('testField');
|
|
var plugin = new TestPlugin();
|
|
|
|
var mockPlugin1 = new goog.testing.LooseMock(plugin);
|
|
mockPlugin1.getTrogClassId().$returns('mockPlugin1');
|
|
mockPlugin1.registerFieldObject(editableField);
|
|
mockPlugin1.isEnabled(editableField).$anyTimes().$returns(true);
|
|
mockPlugin1.isSupportedCommand('+indent').$returns(true);
|
|
mockPlugin1.queryCommandValue('+indent').$returns(true);
|
|
mockPlugin1.activeOnUneditableFields().$returns(true);
|
|
mockPlugin1.$replay();
|
|
|
|
var mockPlugin2 = new goog.testing.LooseMock(plugin);
|
|
mockPlugin2.getTrogClassId().$returns('mockPlugin2');
|
|
mockPlugin2.registerFieldObject(editableField);
|
|
mockPlugin2.isEnabled(editableField).$anyTimes().$returns(true);
|
|
mockPlugin2.$replay();
|
|
|
|
editableField.registerPlugin(mockPlugin1);
|
|
editableField.registerPlugin(mockPlugin2);
|
|
|
|
editableField.queryCommandValue('+indent');
|
|
|
|
mockPlugin1.$verify();
|
|
mockPlugin2.$verify();
|
|
}
|
|
|
|
|
|
/**
|
|
* Tests that if the first plugin does not support a command that its
|
|
* queryCommandValue do not get called and the next plugin can handle the
|
|
* queryCommandValue.
|
|
*/
|
|
function testNotSupportedQueryCommand() {
|
|
var editableField = new FieldConstructor('testField');
|
|
var plugin = new TestPlugin();
|
|
|
|
var mockPlugin1 = new goog.testing.LooseMock(plugin);
|
|
mockPlugin1.getTrogClassId().$returns('mockPlugin1');
|
|
mockPlugin1.registerFieldObject(editableField);
|
|
mockPlugin1.isEnabled(editableField).$anyTimes().$returns(true);
|
|
mockPlugin1.isSupportedCommand('+indent').$returns(false);
|
|
mockPlugin1.$replay();
|
|
|
|
var mockPlugin2 = new goog.testing.LooseMock(plugin);
|
|
mockPlugin2.getTrogClassId().$returns('mockPlugin2');
|
|
mockPlugin2.registerFieldObject(editableField);
|
|
mockPlugin2.isEnabled(editableField).$anyTimes().$returns(true);
|
|
mockPlugin2.isSupportedCommand('+indent').$returns(true);
|
|
mockPlugin2.queryCommandValue('+indent').$returns(true);
|
|
mockPlugin2.activeOnUneditableFields().$returns(true);
|
|
mockPlugin2.$replay();
|
|
|
|
editableField.registerPlugin(mockPlugin1);
|
|
editableField.registerPlugin(mockPlugin2);
|
|
|
|
editableField.queryCommandValue('+indent');
|
|
|
|
mockPlugin1.$verify();
|
|
mockPlugin2.$verify();
|
|
}
|
|
|
|
|
|
/**
|
|
* Tests that if a plugin handles selectionChange that it gets called and
|
|
* no further plugins can handle the selectionChange.
|
|
*/
|
|
function testHandledSelectionChange() {
|
|
var editableField = new FieldConstructor('testField');
|
|
var plugin = new TestPlugin();
|
|
var e = getBrowserEvent();
|
|
|
|
var mockPlugin1 = new goog.testing.LooseMock(plugin);
|
|
mockPlugin1.getTrogClassId().$returns('mockPlugin1');
|
|
mockPlugin1.registerFieldObject(editableField);
|
|
mockPlugin1.isEnabled(editableField).$anyTimes().$returns(true);
|
|
mockPlugin1.handleSelectionChange(e, undefined).$returns(true);
|
|
mockPlugin1.$replay();
|
|
|
|
var mockPlugin2 = new goog.testing.LooseMock(plugin);
|
|
mockPlugin2.getTrogClassId().$returns('mockPlugin2');
|
|
mockPlugin2.registerFieldObject(editableField);
|
|
mockPlugin2.isEnabled(editableField).$anyTimes().$returns(true);
|
|
mockPlugin2.$replay();
|
|
|
|
editableField.registerPlugin(mockPlugin1);
|
|
editableField.registerPlugin(mockPlugin2);
|
|
|
|
editableField.dispatchSelectionChangeEvent(e);
|
|
|
|
mockPlugin1.$verify();
|
|
mockPlugin2.$verify();
|
|
}
|
|
|
|
|
|
/**
|
|
* Tests that if the first plugin does not handle selectionChange that
|
|
* the next plugin gets a chance to handle it.
|
|
*/
|
|
function testNotHandledSelectionChange() {
|
|
var editableField = new FieldConstructor('testField');
|
|
var plugin = new TestPlugin();
|
|
var e = getBrowserEvent();
|
|
|
|
var mockPlugin1 = new goog.testing.LooseMock(plugin);
|
|
mockPlugin1.getTrogClassId().$returns('mockPlugin1');
|
|
mockPlugin1.registerFieldObject(editableField);
|
|
mockPlugin1.isEnabled(editableField).$anyTimes().$returns(true);
|
|
mockPlugin1.handleSelectionChange(e, undefined).$returns(false);
|
|
mockPlugin1.$replay();
|
|
|
|
var mockPlugin2 = new goog.testing.LooseMock(plugin);
|
|
mockPlugin2.getTrogClassId().$returns('mockPlugin2');
|
|
mockPlugin2.registerFieldObject(editableField);
|
|
mockPlugin2.isEnabled(editableField).$anyTimes().$returns(true);
|
|
mockPlugin2.handleSelectionChange(e, undefined).$returns(true);
|
|
mockPlugin2.$replay();
|
|
|
|
editableField.registerPlugin(mockPlugin1);
|
|
editableField.registerPlugin(mockPlugin2);
|
|
|
|
editableField.dispatchSelectionChangeEvent(e);
|
|
|
|
mockPlugin1.$verify();
|
|
mockPlugin2.$verify();
|
|
}
|
|
|
|
|
|
// Tests for goog.editor.Field internals.
|
|
|
|
function testSelectionChange() {
|
|
var editableField = new FieldConstructor('testField', document);
|
|
var clock = new goog.testing.MockClock(true);
|
|
var selectionChanges = goog.testing.recordFunction();
|
|
goog.events.listen(editableField, goog.editor.Field.EventType.SELECTIONCHANGE,
|
|
selectionChanges);
|
|
|
|
editableField.makeEditable();
|
|
|
|
// Emulate pressing left arrow key, this should result in SELECTIONCHANGE
|
|
// event after a short timeout.
|
|
editableField.handleKeyUp_({keyCode: goog.events.KeyCodes.LEFT});
|
|
assertEquals('Selection change should be on a timer', 0,
|
|
selectionChanges.getCallCount());
|
|
clock.tick(1000);
|
|
assertEquals('Selection change should fire within 1s', 1,
|
|
selectionChanges.getCallCount());
|
|
|
|
// Programically place cursor at start. SELECTIONCHANGE event should be fired.
|
|
editableField.placeCursorAtStart();
|
|
assertEquals('Selection change should fire', 2,
|
|
selectionChanges.getCallCount());
|
|
|
|
clock.dispose();
|
|
editableField.dispose();
|
|
}
|
|
|
|
|
|
function testSelectionChangeOnMouseUp() {
|
|
var fakeEvent =
|
|
new goog.events.BrowserEvent({type: 'mouseup', target: 'fakeTarget'});
|
|
var editableField = new FieldConstructor('testField', document);
|
|
var clock = new goog.testing.MockClock(true);
|
|
var selectionChanges = goog.testing.recordFunction();
|
|
goog.events.listen(editableField, goog.editor.Field.EventType.SELECTIONCHANGE,
|
|
selectionChanges);
|
|
|
|
var plugin = new TestPlugin();
|
|
plugin.handleSelectionChange = goog.testing.recordFunction();
|
|
editableField.registerPlugin(plugin);
|
|
|
|
editableField.makeEditable();
|
|
|
|
// Emulate a mouseup event, this should result in an immediate
|
|
// SELECTIONCHANGE, plus a second one in IE after a short timeout.
|
|
editableField.handleMouseUp_(fakeEvent);
|
|
assertEquals('Selection change should fire immediately', 1,
|
|
selectionChanges.getCallCount());
|
|
assertEquals('Plugin should have handled selection change immediately', 1,
|
|
plugin.handleSelectionChange.getCallCount());
|
|
assertEquals('Plugin should have received original browser event to handle',
|
|
fakeEvent, plugin.handleSelectionChange.getLastCall().getArguments()[0]);
|
|
|
|
// Pretend another plugin fired a SELECTIONCHANGE in the meantime.
|
|
editableField.dispatchSelectionChangeEvent();
|
|
assertEquals('Second selection change should fire immediately', 2,
|
|
selectionChanges.getCallCount());
|
|
assertEquals('Plugin should have handled second selection change immediately',
|
|
2, plugin.handleSelectionChange.getCallCount());
|
|
var args = plugin.handleSelectionChange.getLastCall().getArguments();
|
|
assertTrue('Plugin should not have received data from extra firing',
|
|
args.length == 0 ||
|
|
!args[0] && (args.length == 1 || !args[1]));
|
|
|
|
// Now check for the extra call in IE.
|
|
clock.tick(1000);
|
|
if (goog.userAgent.IE) {
|
|
assertEquals('Additional selection change should fire within 1s', 3,
|
|
selectionChanges.getCallCount());
|
|
assertEquals('Plugin should have handled selection change within 1s', 3,
|
|
plugin.handleSelectionChange.getCallCount());
|
|
assertEquals('Plugin should have received target of original browser event',
|
|
fakeEvent.target,
|
|
plugin.handleSelectionChange.getLastCall().getArguments().pop());
|
|
} else {
|
|
assertEquals('No additional selection change should fire', 2,
|
|
selectionChanges.getCallCount());
|
|
assertEquals('Plugin should not have handled selection change again', 2,
|
|
plugin.handleSelectionChange.getCallCount());
|
|
}
|
|
|
|
clock.dispose();
|
|
editableField.dispose();
|
|
}
|
|
|
|
|
|
function testSelectionChangeBeforeUneditable() {
|
|
var editableField = new FieldConstructor('testField', document);
|
|
var clock = new goog.testing.MockClock(true);
|
|
var selectionChanges = goog.testing.recordFunction();
|
|
goog.events.listen(editableField, goog.editor.Field.EventType.SELECTIONCHANGE,
|
|
selectionChanges);
|
|
|
|
editableField.makeEditable();
|
|
editableField.handleKeyUp_({keyCode: goog.events.KeyCodes.LEFT});
|
|
assertEquals('Selection change should be on a timer', 0,
|
|
selectionChanges.getCallCount());
|
|
editableField.makeUneditable();
|
|
assertEquals('Selection change should fire during make uneditable', 1,
|
|
selectionChanges.getCallCount());
|
|
clock.tick(1000);
|
|
assertEquals('No additional selection change should fire', 1,
|
|
selectionChanges.getCallCount());
|
|
|
|
clock.dispose();
|
|
editableField.dispose();
|
|
}
|
|
|
|
|
|
function testGetEditableDomHelper() {
|
|
var editableField = new FieldConstructor('testField', document);
|
|
assertNull('Before being made editable, we do not know the dom helper',
|
|
editableField.getEditableDomHelper());
|
|
editableField.makeEditable();
|
|
assertNotNull('After being made editable, we know the dom helper',
|
|
editableField.getEditableDomHelper());
|
|
assertEquals('Document from domHelper should be the editable elements doc',
|
|
goog.dom.getOwnerDocument(editableField.getElement()),
|
|
editableField.getEditableDomHelper().getDocument());
|
|
editableField.dispose();
|
|
}
|
|
|
|
|
|
function testQueryCommandValue() {
|
|
var editableField = new FieldConstructor('testField', document);
|
|
assertFalse(editableField.queryCommandValue('boo'));
|
|
assertObjectEquals({'boo': false, 'aieee': false},
|
|
editableField.queryCommandValue(['boo', 'aieee']));
|
|
|
|
editableField.makeEditable();
|
|
assertFalse(editableField.queryCommandValue('boo'));
|
|
|
|
editableField.getElement().focus();
|
|
editableField.dispatchSelectionChangeEvent();
|
|
assertNull(editableField.queryCommandValue('boo'));
|
|
assertObjectEquals({'boo': null, 'aieee': null},
|
|
editableField.queryCommandValue(['boo', 'aieee']));
|
|
editableField.dispose();
|
|
}
|
|
|
|
|
|
function testSetHtml() {
|
|
var editableField = new FieldConstructor('testField', document);
|
|
var clock = new goog.testing.MockClock(true);
|
|
|
|
try {
|
|
var delayedChangeCalled = false;
|
|
goog.events.listen(editableField, goog.editor.Field.EventType.DELAYEDCHANGE,
|
|
function() {
|
|
delayedChangeCalled = true;
|
|
});
|
|
|
|
editableField.makeEditable();
|
|
clock.tick(1000);
|
|
assertFalse('Make editable must not fire delayed change.',
|
|
delayedChangeCalled);
|
|
|
|
editableField.setHtml(false, 'bar', true /* Don't fire delayed change */);
|
|
goog.testing.dom.assertHtmlContentsMatch('bar', editableField.getElement());
|
|
clock.tick(1000);
|
|
assertFalse('setHtml must not fire delayed change if so configured.',
|
|
delayedChangeCalled);
|
|
|
|
editableField.setHtml(false, 'foo', false /* Fire delayed change */);
|
|
goog.testing.dom.assertHtmlContentsMatch('foo', editableField.getElement());
|
|
clock.tick(1000);
|
|
assertTrue('setHtml must fire delayed change by default',
|
|
delayedChangeCalled);
|
|
} finally {
|
|
clock.dispose();
|
|
editableField.dispose();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Helper to test that the cursor is placed at the beginning of the editable
|
|
* field's contents.
|
|
* @param {string=} opt_html Html to replace the test file default field
|
|
* contents with.
|
|
* @param {string=} opt_parentId Id of the parent of the node where the cursor
|
|
* is expected to be placed. If omitted, will expect cursor to be placed in
|
|
* the first child of the field element (or, if the field has no content, in
|
|
* the field element itself).
|
|
*/
|
|
function doTestPlaceCursorAtStart(opt_html, opt_parentId) {
|
|
var editableField = new FieldConstructor('testField', document);
|
|
editableField.makeEditable();
|
|
// Initially place selection not at the start of the editable field.
|
|
var textNode = editableField.getElement().firstChild;
|
|
goog.dom.Range.createFromNodes(textNode, 1, textNode, 2).select();
|
|
if (opt_html != null) {
|
|
editableField.getElement().innerHTML = opt_html;
|
|
}
|
|
|
|
editableField.placeCursorAtStart();
|
|
var range = editableField.getRange();
|
|
assertNotNull(
|
|
'Placing the cursor should result in a range object being available',
|
|
range);
|
|
assertTrue('The range should be collapsed', range.isCollapsed());
|
|
textNode = editableField.getElement().firstChild;
|
|
|
|
// We check whether getAttribute exist because textNode may be an actual
|
|
// TextNode, which does not have getAttribute.
|
|
if (textNode && textNode.getAttribute &&
|
|
textNode.getAttribute('_moz_editor_bogus_node')) {
|
|
// At least in FF >= 6, assigning '' to innerHTML of a contentEditable
|
|
// element will results in textNode being modified into:
|
|
// <br _moz_editor_bogus_node="TRUE" _moz_dirty=""> instead of nulling
|
|
// it. So we should null it ourself.
|
|
textNode = null;
|
|
}
|
|
|
|
var startNode = opt_parentId ?
|
|
editableField.getEditableDomHelper().getElement(opt_parentId).firstChild :
|
|
textNode ? textNode : editableField.getElement();
|
|
if (goog.userAgent.WEBKIT &&
|
|
!goog.userAgent.isVersionOrHigher('528')) {
|
|
// Safari 3 seems to normalize the selection to the shallowest endpoint (in
|
|
// this case the editable element) in all cases tested below. This is OK
|
|
// because when you start typing it magically inserts the text at the
|
|
// deepest endpoint, and even behaves as desired in the case tested by
|
|
// testPlaceCursorAtStartNonImportantTextNode.
|
|
startNode = editableField.getElement();
|
|
}
|
|
assertEquals('The range should start at the specified expected node',
|
|
startNode, range.getStartNode());
|
|
assertEquals('The range should start at the beginning of the node',
|
|
0, range.getStartOffset());
|
|
}
|
|
|
|
|
|
/**
|
|
* Verify that restoreSavedRange() restores the range and sets the focus.
|
|
*/
|
|
function testRestoreSavedRange() {
|
|
var editableField = new FieldConstructor('testField', document);
|
|
editableField.makeEditable();
|
|
|
|
|
|
// Create another node to take the focus later.
|
|
var doc = goog.dom.getOwnerDocument(editableField.getElement());
|
|
var otherElem = doc.createElement('div');
|
|
otherElem.tabIndex = '1'; // Make it focusable.
|
|
editableField.getElement().parentNode.appendChild(otherElem);
|
|
|
|
// Initially place selection not at the start of the editable field.
|
|
var textNode = editableField.getElement().firstChild;
|
|
var range = goog.dom.Range.createFromNodes(textNode, 1, textNode, 2);
|
|
range.select();
|
|
var savedRange = goog.editor.range.saveUsingNormalizedCarets(range);
|
|
|
|
// Change range to be a simple cursor at start, and move focus away.
|
|
editableField.placeCursorAtStart();
|
|
otherElem.focus();
|
|
|
|
editableField.restoreSavedRange(savedRange);
|
|
|
|
// Verify that we have focus and the range is restored.
|
|
assertEquals('Field should be focused',
|
|
editableField.getElement(),
|
|
goog.dom.getActiveElement(doc));
|
|
var newRange = editableField.getRange();
|
|
assertEquals('Range startNode', textNode, newRange.getStartNode());
|
|
assertEquals('Range startOffset', 1, newRange.getStartOffset());
|
|
assertEquals('Range endNode', textNode, newRange.getEndNode());
|
|
assertEquals('Range endOffset', 2, newRange.getEndOffset());
|
|
|
|
}
|
|
|
|
|
|
function testPlaceCursorAtStart() {
|
|
doTestPlaceCursorAtStart();
|
|
}
|
|
|
|
|
|
function testPlaceCursorAtStartEmptyField() {
|
|
doTestPlaceCursorAtStart('');
|
|
}
|
|
|
|
|
|
function testPlaceCursorAtStartNonImportantTextNode() {
|
|
doTestPlaceCursorAtStart(
|
|
'\n<span id="important">important text</span>', 'important');
|
|
}
|
|
|
|
|
|
/**
|
|
* Helper to test that the cursor is placed at the beginning of the editable
|
|
* field's contents.
|
|
* @param {string=} opt_html Html to replace the test file default field
|
|
* contents with.
|
|
* @param {string=} opt_parentId Id of the parent of the node where the cursor
|
|
* is expected to be placed. If omitted, will expect cursor to be placed in
|
|
* the first child of the field element (or, if the field has no content, in
|
|
* the field element itself).
|
|
* @param {number=} opt_offset The offset to expect for the end position.
|
|
*/
|
|
function doTestPlaceCursorAtEnd(opt_html, opt_parentId, opt_offset) {
|
|
var editableField = new FieldConstructor('testField', document);
|
|
editableField.makeEditable();
|
|
|
|
// Initially place selection not at the end of the editable field.
|
|
var textNode = editableField.getElement().firstChild;
|
|
goog.dom.Range.createFromNodes(textNode, 0, textNode, 1).select();
|
|
if (opt_html != null) {
|
|
editableField.getElement().innerHTML = opt_html;
|
|
}
|
|
|
|
editableField.placeCursorAtEnd();
|
|
var range = editableField.getRange();
|
|
assertNotNull(
|
|
'Placing the cursor should result in a range object being available',
|
|
range);
|
|
assertTrue('The range should be collapsed', range.isCollapsed());
|
|
textNode = editableField.getElement().firstChild;
|
|
|
|
// We check whether getAttribute exist because textNode may be an actual
|
|
// TextNode, which does not have getAttribute.
|
|
|
|
var hasBogusNode = textNode && textNode.getAttribute &&
|
|
textNode.getAttribute('_moz_editor_bogus_node');
|
|
if (hasBogusNode) {
|
|
// At least in FF >= 6, assigning '' to innerHTML of a contentEditable
|
|
// element will results in textNode being modified into:
|
|
// <br _moz_editor_bogus_node="TRUE" _moz_dirty=""> instead of nulling
|
|
// it. So we should null it ourself.
|
|
textNode = null;
|
|
}
|
|
|
|
var endNode = opt_parentId ?
|
|
editableField.getEditableDomHelper().getElement(opt_parentId).lastChild :
|
|
textNode ? textNode : editableField.getElement();
|
|
assertEquals('The range should end at the specified expected node',
|
|
endNode, range.getEndNode());
|
|
var offset = goog.isDefAndNotNull(opt_offset) ?
|
|
opt_offset :
|
|
textNode ? endNode.nodeValue.length : endNode.childNodes.length - 1;
|
|
if (hasBogusNode) {
|
|
assertEquals('The range should end at the ending of the bogus node ' +
|
|
'added by FF', offset + 1, range.getEndOffset());
|
|
} else {
|
|
assertEquals('The range should end at the ending of the node',
|
|
offset, range.getEndOffset());
|
|
}
|
|
}
|
|
|
|
|
|
function testPlaceCursorAtEnd() {
|
|
doTestPlaceCursorAtEnd();
|
|
}
|
|
|
|
|
|
function testPlaceCursorAtEndEmptyField() {
|
|
doTestPlaceCursorAtEnd('', null, 0);
|
|
}
|
|
|
|
|
|
function testPlaceCursorAtEndNonImportantTextNode() {
|
|
doTestPlaceCursorAtStart(
|
|
'\n<span id="important">important text</span>', 'important');
|
|
}
|
|
|
|
|
|
// Tests related to change/delayed change events.
|
|
|
|
|
|
function testClearDelayedChange() {
|
|
var clock = new goog.testing.MockClock(true);
|
|
var editableField = new FieldConstructor('testField', document);
|
|
editableField.makeEditable();
|
|
|
|
var delayedChangeCalled = false;
|
|
goog.events.listen(editableField, goog.editor.Field.EventType.DELAYEDCHANGE,
|
|
function() {
|
|
delayedChangeCalled = true;
|
|
});
|
|
|
|
// Clears delayed change timer.
|
|
editableField.delayedChangeTimer_.start();
|
|
editableField.clearDelayedChange();
|
|
assertTrue(delayedChangeCalled);
|
|
if (editableField.changeTimerGecko_) {
|
|
assertFalse(editableField.changeTimerGecko_.isActive());
|
|
}
|
|
assertFalse(editableField.delayedChangeTimer_.isActive());
|
|
|
|
// Clears delayed changes caused by changeTimerGecko_
|
|
if (editableField.changeTimerGecko_) {
|
|
delayedChangeCalled = false;
|
|
editableField.changeTimerGecko_.start();
|
|
editableField.clearDelayedChange();
|
|
assertTrue(delayedChangeCalled);
|
|
if (editableField.changeTimerGecko_) {
|
|
assertFalse(editableField.changeTimerGecko_.isActive());
|
|
}
|
|
assertFalse(editableField.delayedChangeTimer_.isActive());
|
|
}
|
|
clock.dispose();
|
|
}
|
|
|
|
|
|
function testHandleChange() {
|
|
if (goog.editor.BrowserFeature.USE_MUTATION_EVENTS) {
|
|
var editableField = new FieldConstructor('testField', document);
|
|
editableField.makeEditable();
|
|
|
|
editableField.changeTimerGecko_.start();
|
|
editableField.handleChange();
|
|
assertFalse(editableField.changeTimerGecko_.isActive());
|
|
}
|
|
}
|
|
|
|
|
|
function testDispatchDelayedChange() {
|
|
var editableField = new FieldConstructor('testField', document);
|
|
editableField.makeEditable();
|
|
|
|
editableField.delayedChangeTimer_.start();
|
|
editableField.dispatchDelayedChange_();
|
|
assertFalse(editableField.delayedChangeTimer_.isActive());
|
|
}
|
|
|
|
|
|
function testHandleWindowLevelMouseUp() {
|
|
var editableField = new FieldConstructor('testField', document);
|
|
if (editableField.usesIframe()) {
|
|
// Only run this test if the editor does not use an iframe.
|
|
return;
|
|
}
|
|
editableField.setUseWindowMouseUp(true);
|
|
editableField.makeEditable();
|
|
var selectionHasFired = false;
|
|
goog.events.listenOnce(
|
|
editableField, goog.editor.Field.EventType.SELECTIONCHANGE,
|
|
function(e) {
|
|
selectionHasFired = true;
|
|
});
|
|
var editableElement = editableField.getElement();
|
|
var otherElement = goog.dom.createDom('div');
|
|
goog.dom.insertSiblingAfter(otherElement, document.body.lastChild);
|
|
|
|
goog.testing.events.fireMouseDownEvent(editableElement);
|
|
assertFalse(selectionHasFired);
|
|
goog.testing.events.fireMouseUpEvent(otherElement);
|
|
assertTrue(selectionHasFired);
|
|
}
|
|
|
|
function testNoHandleWindowLevelMouseUp() {
|
|
var editableField = new FieldConstructor('testField', document);
|
|
editableField.setUseWindowMouseUp(false);
|
|
editableField.makeEditable();
|
|
var selectionHasFired = false;
|
|
goog.events.listenOnce(
|
|
editableField, goog.editor.Field.EventType.SELECTIONCHANGE,
|
|
function(e) {
|
|
selectionHasFired = true;
|
|
});
|
|
var editableElement = editableField.getElement();
|
|
var otherElement = goog.dom.createDom('div');
|
|
goog.dom.insertSiblingAfter(otherElement, document.body.lastChild);
|
|
|
|
goog.testing.events.fireMouseDownEvent(editableElement);
|
|
assertFalse(selectionHasFired);
|
|
goog.testing.events.fireMouseUpEvent(otherElement);
|
|
assertFalse(selectionHasFired);
|
|
}
|
|
|
|
function testIsGeneratingKey() {
|
|
var regularKeyEvent = new goog.events.BrowserEvent();
|
|
regularKeyEvent.charCode = goog.events.KeyCodes.A;
|
|
|
|
var ctrlKeyEvent = new goog.events.BrowserEvent();
|
|
ctrlKeyEvent.ctrlKey = true;
|
|
ctrlKeyEvent.metaKey = true;
|
|
ctrlKeyEvent.charCode = goog.events.KeyCodes.A;
|
|
|
|
var imeKeyEvent = new goog.events.BrowserEvent();
|
|
imeKeyEvent.keyCode = 229; // indicates from an IME - see KEYS_CAUSING_CHANGES
|
|
|
|
assertTrue(goog.editor.Field.isGeneratingKey_(regularKeyEvent, true));
|
|
assertFalse(goog.editor.Field.isGeneratingKey_(ctrlKeyEvent, true));
|
|
if (goog.userAgent.WINDOWS && !goog.userAgent.GECKO) {
|
|
assertTrue(goog.editor.Field.isGeneratingKey_(imeKeyEvent, false));
|
|
} else {
|
|
assertFalse(goog.editor.Field.isGeneratingKey_(imeKeyEvent, false));
|
|
}
|
|
}
|