// Copyright 2008 The Closure Library Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. goog.provide('goog.editor.plugins.AbstractBubblePluginTest'); goog.setTestOnly('goog.editor.plugins.AbstractBubblePluginTest'); goog.require('goog.dom'); goog.require('goog.editor.plugins.AbstractBubblePlugin'); goog.require('goog.events.BrowserEvent'); goog.require('goog.events.KeyCodes'); goog.require('goog.functions'); goog.require('goog.style'); goog.require('goog.testing.editor.FieldMock'); goog.require('goog.testing.editor.TestHelper'); goog.require('goog.testing.events'); goog.require('goog.testing.jsunit'); goog.require('goog.ui.editor.Bubble'); var testHelper; var fieldDiv; var COMMAND = 'base'; var FIELDMOCK; var bubblePlugin; var link; var link2; function setUpPage() { fieldDiv = goog.dom.getElement('field'); var viewportSize = goog.dom.getViewportSize(); // Some tests depends on enough size of viewport. if (viewportSize.width < 600 || viewportSize.height < 440) { window.moveTo(0, 0); window.resizeTo(640, 480); } } function setUp() { testHelper = new goog.testing.editor.TestHelper(fieldDiv); testHelper.setUpEditableElement(); FIELDMOCK = new goog.testing.editor.FieldMock(); bubblePlugin = new goog.editor.plugins.AbstractBubblePlugin(COMMAND); bubblePlugin.fieldObject = FIELDMOCK; fieldDiv.innerHTML = '<a href="http://www.google.com">Google</a>' + '<a href="http://www.google.com">Google2</a>'; link = fieldDiv.firstChild; link2 = fieldDiv.lastChild; window.scrollTo(0, 0); goog.style.setStyle(document.body, 'direction', 'ltr'); goog.style.setStyle(document.getElementById('field'), 'position', 'static'); } function tearDown() { bubblePlugin.closeBubble(); testHelper.tearDownEditableElement(); } /** * This is a helper function for setting up the targetElement with a * given direction. * * @param {string} dir The direction of the targetElement, 'ltr' or 'rtl'. */ function prepareTargetWithGivenDirection(dir) { goog.style.setStyle(document.body, 'direction', dir); fieldDiv.style.direction = dir; fieldDiv.innerHTML = '<a href="http://www.google.com">Google</a>'; link = fieldDiv.firstChild; FIELDMOCK.$replay(); bubblePlugin.createBubbleContents = function(bubbleContainer) { bubbleContainer.innerHTML = '<div style="border:1px solid blue;">B</div>'; goog.style.setStyle(bubbleContainer, 'border', '1px solid white'); }; bubblePlugin.registerFieldObject(FIELDMOCK); bubblePlugin.enable(FIELDMOCK); bubblePlugin.createBubble(link); } function helpTestCreateBubble(opt_fn) { FIELDMOCK.$replay(); var numCalled = 0; bubblePlugin.createBubbleContents = function(bubbleContainer) { numCalled++; assertNotNull('bubbleContainer should not be null', bubbleContainer); }; if (opt_fn) { opt_fn(); } bubblePlugin.createBubble(link); assertEquals('createBubbleContents should be called', 1, numCalled); FIELDMOCK.$verify(); } function testCreateBubble(opt_fn) { helpTestCreateBubble(opt_fn); assertTrue(bubblePlugin.getSharedBubble_() instanceof goog.ui.editor.Bubble); assertTrue('Bubble should be visible', bubblePlugin.isVisible()); } function testOpeningBubbleCallsOnShow() { var numCalled = 0; testCreateBubble(function() { bubblePlugin.onShow = function() { numCalled++; }; }); assertEquals('onShow should be called', 1, numCalled); FIELDMOCK.$verify(); } function testCloseBubble() { testCreateBubble(); bubblePlugin.closeBubble(); assertFalse('Bubble should not be visible', bubblePlugin.isVisible()); FIELDMOCK.$verify(); } function testZindexBehavior() { // Don't use the default return values. FIELDMOCK.$reset(); FIELDMOCK.getAppWindow().$anyTimes().$returns(window); FIELDMOCK.getEditableDomHelper().$anyTimes() .$returns(goog.dom.getDomHelper(document)); FIELDMOCK.getBaseZindex().$returns(2); bubblePlugin.createBubbleContents = goog.nullFunction; FIELDMOCK.$replay(); bubblePlugin.createBubble(link); assertEquals('2', '' + bubblePlugin.getSharedBubble_().bubbleContainer_.style.zIndex); FIELDMOCK.$verify(); } function testNoTwoBubblesOpenAtSameTime() { FIELDMOCK.$replay(); var origClose = goog.bind(bubblePlugin.closeBubble, bubblePlugin); var numTimesCloseCalled = 0; bubblePlugin.closeBubble = function() { numTimesCloseCalled++; origClose(); }; bubblePlugin.getBubbleTargetFromSelection = goog.functions.identity; bubblePlugin.createBubbleContents = goog.nullFunction; bubblePlugin.handleSelectionChangeInternal(link); assertEquals(0, numTimesCloseCalled); assertEquals(link, bubblePlugin.targetElement_); FIELDMOCK.$verify(); bubblePlugin.handleSelectionChangeInternal(link2); assertEquals(1, numTimesCloseCalled); assertEquals(link2, bubblePlugin.targetElement_); FIELDMOCK.$verify(); } function testHandleSelectionChangeWithEvent() { FIELDMOCK.$replay(); var fakeEvent = new goog.events.BrowserEvent({type: 'mouseup', target: link}); bubblePlugin.getBubbleTargetFromSelection = goog.functions.identity; bubblePlugin.createBubbleContents = goog.nullFunction; bubblePlugin.handleSelectionChange(fakeEvent); assertTrue('Bubble should have been opened', bubblePlugin.isVisible()); assertEquals('Bubble target should be provided event\'s target', link, bubblePlugin.targetElement_); } function testHandleSelectionChangeWithTarget() { FIELDMOCK.$replay(); bubblePlugin.getBubbleTargetFromSelection = goog.functions.identity; bubblePlugin.createBubbleContents = goog.nullFunction; bubblePlugin.handleSelectionChange(undefined, link2); assertTrue('Bubble should have been opened', bubblePlugin.isVisible()); assertEquals('Bubble target should be provided target', link2, bubblePlugin.targetElement_); } /** * Regression test for @bug 2945341 */ function testSelectOneTextCharacterNoError() { FIELDMOCK.$replay(); bubblePlugin.getBubbleTargetFromSelection = goog.functions.identity; bubblePlugin.createBubbleContents = goog.nullFunction; // Select first char of first link's text node. testHelper.select(link.firstChild, 0, link.firstChild, 1); // This should execute without js errors. bubblePlugin.handleSelectionChange(); assertTrue('Bubble should have been opened', bubblePlugin.isVisible()); FIELDMOCK.$verify(); } function testTabKeyEvents() { FIELDMOCK.focus(); FIELDMOCK.$replay(); bubblePlugin.enableKeyboardNavigation(true /* enable link tabbing */); bubblePlugin.getBubbleTargetFromSelection = goog.functions.identity; bubblePlugin.createBubbleContents = function(container) { this.createLink('linkInBubble0', 'Foo', false, container); this.createLink('linkInBubble1', 'Bar', false, container); }; bubblePlugin.handleSelectionChangeInternal(link); goog.testing.events.fireKeySequence(fieldDiv, goog.events.KeyCodes.TAB); assertTrue('Bubble should be visible', bubblePlugin.isVisible()); var linkEls = goog.dom.getElementsByClass( goog.editor.plugins.AbstractBubblePlugin.LINK_CLASSNAME_, bubblePlugin.getSharedBubble_().getContentElement()); goog.testing.events.fireKeySequence(linkEls[0], goog.events.KeyCodes.TAB); goog.testing.events.fireKeySequence(linkEls[1], goog.events.KeyCodes.TAB); FIELDMOCK.$verify(); } function testTabKeyEventsWithShiftKey() { FIELDMOCK.focus(); FIELDMOCK.$replay(); bubblePlugin.enableKeyboardNavigation(true /* enable link tabbing */); bubblePlugin.getBubbleTargetFromSelection = goog.functions.identity; bubblePlugin.createBubbleContents = function(container) { this.createLink('linkInBubble0', 'Foo', false, container); this.createLink('linkInBubble1', 'Bar', false, container); }; bubblePlugin.handleSelectionChangeInternal(link); goog.testing.events.fireKeySequence(fieldDiv, goog.events.KeyCodes.TAB); assertTrue('Bubble should be visible', bubblePlugin.isVisible()); var linkEls = goog.dom.getElementsByClass( goog.editor.plugins.AbstractBubblePlugin.LINK_CLASSNAME_, bubblePlugin.getSharedBubble_().getContentElement()); goog.testing.events.fireKeySequence(linkEls[0], goog.events.KeyCodes.TAB, {shiftKey: true}); FIELDMOCK.$verify(); } function testTabKeyNoEffectKeyboardNavDisabled() { FIELDMOCK.$replay(); bubblePlugin.getBubbleTargetFromSelection = goog.functions.identity; bubblePlugin.createBubbleContents = function(container) { this.createLink('linkInBubble0', 'Foo', false, container); this.createLink('linkInBubble1', 'Bar', false, container); }; bubblePlugin.handleSelectionChangeInternal(link); assertFalse('The action should not be handled by the plugin', bubblePlugin.handleKeyDown({ keyCode: goog.events.KeyCodes.TAB, shiftKey: false })); FIELDMOCK.$verify(); } function testOtherKeyEventNoEffectKeyboardNavEnabled() { FIELDMOCK.$replay(); bubblePlugin.enableKeyboardNavigation(true /* enable link tabbing */); bubblePlugin.getBubbleTargetFromSelection = goog.functions.identity; bubblePlugin.createBubbleContents = function(container) { this.createLink('linkInBubble0', 'Foo', false, container); this.createLink('linkInBubble1', 'Bar', false, container); }; bubblePlugin.handleSelectionChangeInternal(link); // Test pressing CTRL + B: this should not have any effect. goog.testing.events.fireKeySequence(fieldDiv, goog.events.KeyCodes.B, {ctrlKey: true}); assertTrue('Bubble should be visible', bubblePlugin.isVisible()); assertFalse('The action should not be handled by the plugin', bubblePlugin.handleKeyDown({ keyCode: goog.events.KeyCodes.B, shiftKey: false, ctrlKey: true })); FIELDMOCK.$verify(); }