blob: c878c9bebf7b99008577eb41c3b80aa53450bc9d [file] [log] [blame]
// 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.LinkBubbleTest');
goog.setTestOnly('goog.editor.plugins.LinkBubbleTest');
goog.require('goog.dom');
goog.require('goog.dom.Range');
goog.require('goog.dom.TagName');
goog.require('goog.editor.Command');
goog.require('goog.editor.Link');
goog.require('goog.editor.plugins.LinkBubble');
goog.require('goog.events.BrowserEvent');
goog.require('goog.events.Event');
goog.require('goog.events.EventType');
goog.require('goog.string');
goog.require('goog.style');
goog.require('goog.testing.FunctionMock');
goog.require('goog.testing.PropertyReplacer');
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.userAgent');
var fieldDiv;
var FIELDMOCK;
var linkBubble;
var link;
var mockWindowOpen;
var stubs;
var testHelper;
function setUpPage() {
fieldDiv = goog.dom.$('field');
stubs = new goog.testing.PropertyReplacer();
testHelper = new goog.testing.editor.TestHelper(goog.dom.getElement('field'));
}
function setUp() {
testHelper.setUpEditableElement();
FIELDMOCK = new goog.testing.editor.FieldMock();
linkBubble = new goog.editor.plugins.LinkBubble();
linkBubble.fieldObject = FIELDMOCK;
link = fieldDiv.firstChild;
mockWindowOpen = new goog.testing.FunctionMock('open');
stubs.set(window, 'open', mockWindowOpen);
}
function tearDown() {
linkBubble.closeBubble();
testHelper.tearDownEditableElement();
stubs.reset();
}
function testLinkSelected() {
FIELDMOCK.$replay();
linkBubble.enable(FIELDMOCK);
goog.dom.Range.createFromNodeContents(link).select();
linkBubble.handleSelectionChange();
assertBubble();
FIELDMOCK.$verify();
}
function testLinkClicked() {
FIELDMOCK.$replay();
linkBubble.enable(FIELDMOCK);
linkBubble.handleSelectionChange(createMouseEvent(link));
assertBubble();
FIELDMOCK.$verify();
}
function testImageLink() {
FIELDMOCK.$replay();
linkBubble.enable(FIELDMOCK);
link.setAttribute('imageanchor', 1);
linkBubble.handleSelectionChange(createMouseEvent(link));
assertBubble();
FIELDMOCK.$verify();
}
function closeBox() {
var closeBox = goog.dom.getElementsByTagNameAndClass(goog.dom.TagName.DIV,
'tr_bubble_closebox');
assertEquals('Should find only one close box', 1, closeBox.length);
assertNotNull('Found close box', closeBox[0]);
goog.testing.events.fireClickSequence(closeBox[0]);
}
function testCloseBox() {
testLinkClicked();
closeBox();
assertNoBubble();
FIELDMOCK.$verify();
}
function testChangeClicked() {
FIELDMOCK.execCommand(goog.editor.Command.MODAL_LINK_EDITOR,
new goog.editor.Link(link, false));
FIELDMOCK.$registerArgumentListVerifier('execCommand', function(arr1, arr2) {
return arr1.length == arr2.length &&
arr1.length == 2 &&
arr1[0] == goog.editor.Command.MODAL_LINK_EDITOR &&
arr2[0] == goog.editor.Command.MODAL_LINK_EDITOR &&
arr1[1] instanceof goog.editor.Link &&
arr2[1] instanceof goog.editor.Link;
});
FIELDMOCK.$times(1);
FIELDMOCK.$returns(true);
FIELDMOCK.$replay();
linkBubble.enable(FIELDMOCK);
linkBubble.handleSelectionChange(createMouseEvent(link));
assertBubble();
goog.testing.events.fireClickSequence(
goog.dom.$(goog.editor.plugins.LinkBubble.CHANGE_LINK_ID_));
assertNoBubble();
FIELDMOCK.$verify();
}
function testDeleteClicked() {
FIELDMOCK.dispatchBeforeChange();
FIELDMOCK.$times(1);
FIELDMOCK.dispatchChange();
FIELDMOCK.$times(1);
FIELDMOCK.focus();
FIELDMOCK.$times(1);
FIELDMOCK.$replay();
linkBubble.enable(FIELDMOCK);
linkBubble.handleSelectionChange(createMouseEvent(link));
assertBubble();
goog.testing.events.fireClickSequence(
goog.dom.$(goog.editor.plugins.LinkBubble.DELETE_LINK_ID_));
var element = goog.userAgent.GECKO ? document.body : fieldDiv;
assertNotEquals('Link removed', element.firstChild.nodeName,
goog.dom.TagName.A);
assertNoBubble();
FIELDMOCK.$verify();
}
function testActionClicked() {
var SPAN = 'actionSpanId';
var LINK = 'actionLinkId';
var toShowCount = 0;
var actionCount = 0;
var linkAction = new goog.editor.plugins.LinkBubble.Action(
SPAN, LINK, 'message',
function() {
toShowCount++;
return toShowCount == 1; // Show it the first time.
},
function() {
actionCount++;
});
linkBubble = new goog.editor.plugins.LinkBubble(linkAction);
linkBubble.fieldObject = FIELDMOCK;
FIELDMOCK.$replay();
linkBubble.enable(FIELDMOCK);
// The first time the bubble is shown, show our custom action.
linkBubble.handleSelectionChange(createMouseEvent(link));
assertBubble();
assertEquals('Should check showing the action', 1, toShowCount);
assertEquals('Action should not have fired yet', 0, actionCount);
assertTrue('Action should be visible 1st time', goog.style.isElementShown(
goog.dom.$(SPAN)));
goog.testing.events.fireClickSequence(goog.dom.$(LINK));
assertEquals('Should not check showing again yet', 1, toShowCount);
assertEquals('Action should be fired', 1, actionCount);
closeBox();
assertNoBubble();
// The action won't be shown the second time around.
linkBubble.handleSelectionChange(createMouseEvent(link));
assertBubble();
assertEquals('Should check showing again', 2, toShowCount);
assertEquals('Action should not fire again', 1, actionCount);
assertFalse('Action should not be shown 2nd time', goog.style.isElementShown(
goog.dom.$(SPAN)));
FIELDMOCK.$verify();
}
function testLinkTextClicked() {
mockWindowOpen('http://www.google.com/', '_blank', '');
mockWindowOpen.$replay();
FIELDMOCK.$replay();
linkBubble.enable(FIELDMOCK);
linkBubble.handleSelectionChange(createMouseEvent(link));
assertBubble();
goog.testing.events.fireClickSequence(
goog.dom.$(goog.editor.plugins.LinkBubble.TEST_LINK_ID_));
assertBubble();
mockWindowOpen.$verify();
FIELDMOCK.$verify();
}
function testLinkTextClickedCustomUrlFn() {
mockWindowOpen('http://images.google.com/', '_blank', '');
mockWindowOpen.$replay();
FIELDMOCK.$replay();
linkBubble.enable(FIELDMOCK);
linkBubble.setTestLinkUrlFn(function(url) {
return url.replace('www', 'images');
});
linkBubble.handleSelectionChange(createMouseEvent(link));
assertBubble();
goog.testing.events.fireClickSequence(
goog.dom.$(goog.editor.plugins.LinkBubble.TEST_LINK_ID_));
assertBubble();
mockWindowOpen.$verify();
FIELDMOCK.$verify();
}
/**
* Urls with invalid schemes shouldn't be linkified.
* @bug 2585360
*/
function testDontLinkifyInvalidScheme() {
mockWindowOpen.$replay();
FIELDMOCK.$replay();
linkBubble.enable(FIELDMOCK);
var badLink = document.createElement('a');
badLink.href = 'javascript:alert(1)';
badLink.innerHTML = 'bad link';
linkBubble.handleSelectionChange(createMouseEvent(badLink));
assertBubble();
// The link shouldn't exist at all
assertNull(goog.dom.$(goog.editor.plugins.LinkBubble.TEST_LINK_ID_));
assertBubble();
mockWindowOpen.$verify();
FIELDMOCK.$verify();
}
function testIsSafeSchemeToOpen() {
// Urls with no scheme at all are ok too since 'http://' will be prepended.
var good = [
'http://google.com', 'http://google.com/', 'https://google.com',
'null@google.com', 'http://www.google.com', 'http://site.com',
'google.com', 'google', 'http://google', 'HTTP://GOOGLE.COM',
'HtTp://www.google.com'
];
var bad = [
'javascript:google.com', 'httpp://google.com', 'data:foo',
'javascript:alert(\'hi\');', 'abc:def'
];
for (var i = 0; i < good.length; i++) {
assertTrue(good[i] + ' should have a safe scheme',
linkBubble.isSafeSchemeToOpen_(good[i]));
}
for (i = 0; i < bad.length; i++) {
assertFalse(bad[i] + ' should have an unsafe scheme',
linkBubble.isSafeSchemeToOpen_(bad[i]));
}
}
function testShouldOpenWithWhitelist() {
linkBubble.setSafeToOpenSchemes(['abc']);
assertTrue('Scheme should be safe',
linkBubble.shouldOpenUrl('abc://google.com'));
assertFalse('Scheme should be unsafe',
linkBubble.shouldOpenUrl('http://google.com'));
linkBubble.setBlockOpeningUnsafeSchemes(false);
assertTrue('Non-whitelisted should now be safe after disabling blocking',
linkBubble.shouldOpenUrl('http://google.com'));
}
/**
* @bug 763211
* @bug 2182147
*/
function testLongUrlTestLinkAnchorTextCorrect() {
FIELDMOCK.$replay();
linkBubble.enable(FIELDMOCK);
var longUrl = 'http://www.reallylonglinkthatshouldbetruncated' +
'becauseitistoolong.com';
var truncatedLongUrl = goog.string.truncateMiddle(longUrl, 48);
var longLink = document.createElement('a');
longLink.href = longUrl;
longLink.innerHTML = 'Google';
fieldDiv.appendChild(longLink);
linkBubble.handleSelectionChange(createMouseEvent(longLink));
assertBubble();
var testLinkEl = goog.dom.$(goog.editor.plugins.LinkBubble.TEST_LINK_ID_);
assertEquals(
'The test link\'s anchor text should be the truncated URL.',
truncatedLongUrl,
testLinkEl.innerHTML);
fieldDiv.removeChild(longLink);
FIELDMOCK.$verify();
}
/**
* @bug 2416024
*/
function testOverridingCreateBubbleContentsDoesntNpeGetTargetUrl() {
FIELDMOCK.$replay();
linkBubble.enable(FIELDMOCK);
stubs.set(linkBubble, 'createBubbleContents',
function(elem) {
// getTargetUrl would cause an NPE if urlUtil_ wasn't defined yet.
linkBubble.getTargetUrl();
});
assertNotThrows('Accessing this.urlUtil_ should not NPE',
goog.bind(linkBubble.handleSelectionChange,
linkBubble, createMouseEvent(link)));
FIELDMOCK.$verify();
}
/**
* @bug 15379294
*/
function testUpdateLinkCommandDoesNotTriggerAnException() {
FIELDMOCK.$replay();
linkBubble.enable(FIELDMOCK);
// At this point, the bubble was not created yet using its createBubble
// public method.
assertNotThrows(
'Executing goog.editor.Command.UPDATE_LINK_BUBBLE should not trigger ' +
'an exception even if the bubble was not created yet using its ' +
'createBubble method.',
goog.bind(linkBubble.execCommandInternal, linkBubble,
goog.editor.Command.UPDATE_LINK_BUBBLE));
FIELDMOCK.$verify();
}
function assertBubble() {
assertTrue('Link bubble visible', linkBubble.isVisible());
assertNotNull('Link bubble created',
goog.dom.$(goog.editor.plugins.LinkBubble.LINK_DIV_ID_));
}
function assertNoBubble() {
assertFalse('Link bubble not visible', linkBubble.isVisible());
assertNull('Link bubble not created',
goog.dom.$(goog.editor.plugins.LinkBubble.LINK_DIV_ID_));
}
function createMouseEvent(target) {
var eventObj = new goog.events.Event(goog.events.EventType.MOUSEUP, target);
eventObj.button = goog.events.BrowserEvent.MouseButton.LEFT;
return new goog.events.BrowserEvent(eventObj, target);
}