// 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.BasicTextFormatterTest'); goog.setTestOnly('goog.editor.plugins.BasicTextFormatterTest'); goog.require('goog.array'); goog.require('goog.dom'); goog.require('goog.dom.Range'); goog.require('goog.dom.TagName'); goog.require('goog.editor.BrowserFeature'); goog.require('goog.editor.Command'); goog.require('goog.editor.Field'); goog.require('goog.editor.Plugin'); goog.require('goog.editor.plugins.BasicTextFormatter'); goog.require('goog.object'); goog.require('goog.style'); goog.require('goog.testing.ExpectedFailures'); goog.require('goog.testing.LooseMock'); goog.require('goog.testing.PropertyReplacer'); goog.require('goog.testing.editor.FieldMock'); goog.require('goog.testing.editor.TestHelper'); goog.require('goog.testing.jsunit'); goog.require('goog.testing.mockmatchers'); goog.require('goog.userAgent'); goog.require('goog.userAgent.product'); var stubs; var SAVED_HTML; var FIELDMOCK; var FORMATTER; var ROOT; var HELPER; var OPEN_SUB; var CLOSE_SUB; var OPEN_SUPER; var CLOSE_SUPER; var MOCK_BLOCKQUOTE_STYLE = 'border-left: 1px solid gray;'; var MOCK_GET_BLOCKQUOTE_STYLES = function() { return MOCK_BLOCKQUOTE_STYLE; }; var REAL_FIELD; var REAL_PLUGIN; var expectedFailures; function setUpPage() { stubs = new goog.testing.PropertyReplacer(); SAVED_HTML = goog.dom.getElement('html').innerHTML; FIELDMOCK; FORMATTER; ROOT = goog.dom.getElement('root'); HELPER; OPEN_SUB = goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('530') ? '' : ''; CLOSE_SUB = goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('530') ? '' : ''; OPEN_SUPER = goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('530') ? '' : ''; CLOSE_SUPER = goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('530') ? '' : ''; expectedFailures = new goog.testing.ExpectedFailures(); } function setUp() { FIELDMOCK = new goog.testing.editor.FieldMock(); FORMATTER = new goog.editor.plugins.BasicTextFormatter(); FORMATTER.fieldObject = FIELDMOCK; } function setUpRealField() { REAL_FIELD = new goog.editor.Field('real-field'); REAL_PLUGIN = new goog.editor.plugins.BasicTextFormatter(); REAL_FIELD.registerPlugin(REAL_PLUGIN); REAL_FIELD.makeEditable(); } function setUpRealFieldIframe() { REAL_FIELD = new goog.editor.Field('iframe'); FORMATTER = new goog.editor.plugins.BasicTextFormatter(); REAL_FIELD.registerPlugin(FORMATTER); REAL_FIELD.makeEditable(); } function selectRealField() { goog.dom.Range.createFromNodeContents(REAL_FIELD.getElement()).select(); REAL_FIELD.dispatchSelectionChangeEvent(); } function tearDown() { tearDownFontSizeTests(); if (REAL_FIELD) { REAL_FIELD.makeUneditable(); REAL_FIELD.dispose(); REAL_FIELD = null; } expectedFailures.handleTearDown(); stubs.reset(); goog.dom.getElement('html').innerHTML = SAVED_HTML; } function setUpListAndBlockquoteTests() { var htmlDiv = document.getElementById('html'); HELPER = new goog.testing.editor.TestHelper(htmlDiv); HELPER.setUpEditableElement(); FIELDMOCK.getElement(); FIELDMOCK.$anyTimes(); FIELDMOCK.$returns(htmlDiv); } function tearDownHelper() { HELPER.tearDownEditableElement(); HELPER.dispose(); HELPER = null; } function tearDownListAndBlockquoteTests() { tearDownHelper(); } function testIEList() { if (goog.userAgent.IE) { setUpListAndBlockquoteTests(); FIELDMOCK.queryCommandValue('rtl').$returns(null); FIELDMOCK.$replay(); var ul = goog.dom.getElement('outerUL'); goog.dom.Range .createFromNodeContents(goog.dom.getFirstElementChild(ul).firstChild) .select(); FORMATTER.fixIELists_(); assertFalse('Unordered list must not have ordered type', ul.type == '1'); var ol = goog.dom.getElement('ol'); ol.type = 'disc'; goog.dom.Range .createFromNodeContents(goog.dom.getFirstElementChild(ul).firstChild) .select(); FORMATTER.fixIELists_(); assertFalse('Ordered list must not have unordered type', ol.type == 'disc'); ol.type = '1'; goog.dom.Range .createFromNodeContents(goog.dom.getFirstElementChild(ul).firstChild) .select(); FORMATTER.fixIELists_(); assertTrue('Ordered list must retain ordered list type', ol.type == '1'); tearDownListAndBlockquoteTests(); } } function testWebKitList() { if (goog.userAgent.WEBKIT) { setUpListAndBlockquoteTests(); FIELDMOCK.queryCommandValue('rtl').$returns(null); FIELDMOCK.$replay(); var ul = document.getElementById('outerUL'); var html = ul.innerHTML; goog.dom.Range.createFromNodeContents(ul).select(); FORMATTER.fixSafariLists_(); assertEquals('Contents of UL shouldn\'t change', html, ul.innerHTML); ul = document.getElementById('outerUL2'); goog.dom.Range.createFromNodeContents(ul).select(); FORMATTER.fixSafariLists_(); var childULs = goog.dom.getElementsByTagName(goog.dom.TagName.UL, ul); assertEquals('UL should have one child UL', 1, childULs.length); tearDownListAndBlockquoteTests(); } } function testGeckoListFont() { if (goog.userAgent.GECKO) { setUpListAndBlockquoteTests(); FIELDMOCK.queryCommandValue(goog.editor.Command.DEFAULT_TAG) .$returns('BR') .$times(2); FIELDMOCK.$replay(); var p = goog.dom.getElement('geckolist'); var font = p.firstChild; goog.dom.Range.createFromNodeContents(font).select(); retVal = FORMATTER.beforeInsertListGecko_(); assertFalse('Workaround shouldn\'t be applied when not needed', retVal); goog.dom.removeChildren(font); goog.dom.Range.createFromNodeContents(font).select(); var retVal = FORMATTER.beforeInsertListGecko_(); assertTrue('Workaround should be applied when needed', retVal); document.execCommand('insertorderedlist', false, true); assertTrue( 'Font should be Courier', /courier/i.test(document.queryCommandValue('fontname'))); tearDownListAndBlockquoteTests(); } } function testSwitchListType() { if (!goog.userAgent.WEBKIT) { return; } // Test that we're not seeing https://bugs.webkit.org/show_bug.cgi?id=19539, // the type of multi-item lists. setUpListAndBlockquoteTests(); FIELDMOCK.$replay(); var list = goog.dom.getElement('switchListType'); var parent = goog.dom.getParentElement(list); goog.dom.Range.createFromNodeContents(list).select(); FORMATTER.execCommandInternal('insertunorderedlist'); list = goog.dom.getFirstElementChild(parent); assertEquals(String(goog.dom.TagName.UL), list.tagName); assertEquals( 3, goog.dom.getElementsByTagNameAndClass(goog.dom.TagName.LI, null, list) .length); goog.dom.Range.createFromNodeContents(list).select(); FORMATTER.execCommandInternal('insertorderedlist'); list = goog.dom.getFirstElementChild(parent); assertEquals(String(goog.dom.TagName.OL), list.tagName); assertEquals( 3, goog.dom.getElementsByTagNameAndClass(goog.dom.TagName.LI, null, list) .length); tearDownListAndBlockquoteTests(); } function testIsSilentCommand() { var commands = goog.object.getValues(goog.editor.plugins.BasicTextFormatter.COMMAND); var silentCommands = [goog.editor.plugins.BasicTextFormatter.COMMAND.CREATE_LINK]; for (var i = 0; i < commands.length; i += 1) { var command = commands[i]; var shouldBeSilent = goog.array.contains(silentCommands, command); var isSilent = goog.editor.plugins.BasicTextFormatter.prototype.isSilentCommand.call( null, command); assertEquals(shouldBeSilent, isSilent); } } function setUpSubSuperTests() { goog.dom.setTextContent(ROOT, '12345'); HELPER = new goog.testing.editor.TestHelper(ROOT); HELPER.setUpEditableElement(); } function tearDownSubSuperTests() { tearDownHelper(); } function testSubscriptRemovesSuperscript() { setUpSubSuperTests(); FIELDMOCK.$replay(); HELPER.select('12345', 1, '12345', 4); // Selects '234'. FORMATTER.execCommandInternal( goog.editor.plugins.BasicTextFormatter.COMMAND.SUPERSCRIPT); HELPER.assertHtmlMatches('1' + OPEN_SUPER + '234' + CLOSE_SUPER + '5'); FORMATTER.execCommandInternal( goog.editor.plugins.BasicTextFormatter.COMMAND.SUBSCRIPT); HELPER.assertHtmlMatches('1' + OPEN_SUB + '234' + CLOSE_SUB + '5'); FIELDMOCK.$verify(); tearDownSubSuperTests(); } function testSuperscriptRemovesSubscript() { setUpSubSuperTests(); FIELDMOCK.$replay(); HELPER.select('12345', 1, '12345', 4); // Selects '234'. FORMATTER.execCommandInternal( goog.editor.plugins.BasicTextFormatter.COMMAND.SUBSCRIPT); HELPER.assertHtmlMatches('1' + OPEN_SUB + '234' + CLOSE_SUB + '5'); FORMATTER.execCommandInternal( goog.editor.plugins.BasicTextFormatter.COMMAND.SUPERSCRIPT); HELPER.assertHtmlMatches('1' + OPEN_SUPER + '234' + CLOSE_SUPER + '5'); FIELDMOCK.$verify(); tearDownSubSuperTests(); } function testSubscriptRemovesSuperscriptIntersecting() { // Tests: 12345 , sup(23) , sub(34) ==> 1+sup(2)+sub(34)+5 // This is more complex because the sub and sup calls are made on separate // fields which intersect each other and queryCommandValue seems to return // false if the command is only applied to part of the field. setUpSubSuperTests(); FIELDMOCK.$replay(); HELPER.select('12345', 1, '12345', 3); // Selects '23'. FORMATTER.execCommandInternal( goog.editor.plugins.BasicTextFormatter.COMMAND.SUPERSCRIPT); HELPER.assertHtmlMatches('1' + OPEN_SUPER + '23' + CLOSE_SUPER + '45'); HELPER.select('23', 1, '45', 1); // Selects '34'. FORMATTER.execCommandInternal( goog.editor.plugins.BasicTextFormatter.COMMAND.SUBSCRIPT); HELPER.assertHtmlMatches( '1' + OPEN_SUPER + '2' + CLOSE_SUPER + OPEN_SUB + '34' + CLOSE_SUB + '5'); FIELDMOCK.$verify(); tearDownSubSuperTests(); } function testSuperscriptRemovesSubscriptIntersecting() { // Tests: 12345 , sub(23) , sup(34) ==> 1+sub(2)+sup(34)+5 setUpSubSuperTests(); FIELDMOCK.$replay(); HELPER.select('12345', 1, '12345', 3); // Selects '23'. FORMATTER.execCommandInternal( goog.editor.plugins.BasicTextFormatter.COMMAND.SUBSCRIPT); HELPER.assertHtmlMatches('1' + OPEN_SUB + '23' + CLOSE_SUB + '45'); HELPER.select('23', 1, '45', 1); // Selects '34'. FORMATTER.execCommandInternal( goog.editor.plugins.BasicTextFormatter.COMMAND.SUPERSCRIPT); HELPER.assertHtmlMatches( '1' + OPEN_SUB + '2' + CLOSE_SUB + OPEN_SUPER + '34' + CLOSE_SUPER + '5'); FIELDMOCK.$verify(); tearDownSubSuperTests(); } function setUpLinkTests(text, url, isEditable) { stubs.set(window, 'prompt', function() { return url; }); ROOT.innerHTML = text; HELPER = new goog.testing.editor.TestHelper(ROOT); if (isEditable) { HELPER.setUpEditableElement(); FIELDMOCK .execCommand( goog.editor.Command.MODAL_LINK_EDITOR, goog.testing.mockmatchers.isObject) .$returns(undefined); FORMATTER.focusField_ = function() { throw 'Field should not be re-focused'; }; } FIELDMOCK.getElement().$anyTimes().$returns(ROOT); FIELDMOCK.setModalMode(true); FIELDMOCK.isSelectionEditable().$anyTimes().$returns(isEditable); } function tearDownLinkTests() { tearDownHelper(); } function testLink() { setUpLinkTests('12345', 'http://www.x.com/', true); FIELDMOCK.$replay(); HELPER.select('12345', 3); FORMATTER.execCommandInternal(goog.editor.Command.LINK); HELPER.assertHtmlMatches( goog.editor.BrowserFeature.GETS_STUCK_IN_LINKS ? '123http://www.x.com/ 45' : '123http://www.x.com/45'); FIELDMOCK.$verify(); tearDownLinkTests(); } function testLinks() { var url1 = 'http://google.com/1'; var url2 = 'http://google.com/2'; var dialogUrl = 'http://google.com/3'; var html = '
' + url1 + '
' + url2 + '
'; setUpLinkTests(html, dialogUrl, true); FIELDMOCK.$replay(); HELPER.select(url1, 0, url2, url2.length); FORMATTER.execCommandInternal(goog.editor.Command.LINK); var expectDialogUrl = false; if (goog.userAgent.IE || (goog.userAgent.EDGE && !goog.userAgent.product.isVersion(14))) { expectDialogUrl = true; } HELPER.assertHtmlMatches( '' + '' + (expectDialogUrl ? dialogUrl : url2) + '
'); } function testSelectedLink() { setUpLinkTests('12345', 'http://www.x.com/', true); FIELDMOCK.$replay(); HELPER.select('12345', 1, '12345', 4); FORMATTER.execCommandInternal(goog.editor.Command.LINK); HELPER.assertHtmlMatches( goog.editor.BrowserFeature.GETS_STUCK_IN_LINKS ? '1234 5' : '12345'); FIELDMOCK.$verify(); tearDownLinkTests(); } function testCanceledLink() { setUpLinkTests('12345', undefined, true); FIELDMOCK.$replay(); HELPER.select('12345', 1, '12345', 4); FORMATTER.execCommandInternal(goog.editor.Command.LINK); HELPER.assertHtmlMatches('12345'); assertEquals('234', FIELDMOCK.getRange().getText()); FIELDMOCK.$verify(); tearDownLinkTests(); } function testUnfocusedLink() { FIELDMOCK.$reset(); FIELDMOCK.getEditableDomHelper().$anyTimes().$returns( goog.dom.getDomHelper(window.document)); setUpLinkTests('12345', undefined, false); FIELDMOCK.getRange().$anyTimes().$returns(null); FIELDMOCK.$replay(); FORMATTER.execCommandInternal(goog.editor.Command.LINK); HELPER.assertHtmlMatches('12345'); FIELDMOCK.$verify(); tearDownLinkTests(); } function testCreateLink() { var text = 'some text here'; var url = 'http://google.com'; ROOT.innerHTML = text; HELPER = new goog.testing.editor.TestHelper(ROOT); HELPER.setUpEditableElement(); FIELDMOCK.isSelectionEditable().$anyTimes().$returns(true); FIELDMOCK.getElement().$anyTimes().$returns(ROOT); FIELDMOCK.$replay(); HELPER.select(text, 0, text, text.length); FORMATTER.execCommandInternal( goog.editor.plugins.BasicTextFormatter.COMMAND.CREATE_LINK, FIELDMOCK.getRange(), url); HELPER.assertHtmlMatches('' + text + ''); FIELDMOCK.$verify(); tearDownLinkTests(); } function setUpJustifyTests(html) { ROOT.innerHTML = html; HELPER = new goog.testing.editor.TestHelper(ROOT); HELPER.setUpEditableElement(ROOT); FIELDMOCK.getElement(); FIELDMOCK.$anyTimes(); FIELDMOCK.$returns(ROOT); FIELDMOCK.getElement(); FIELDMOCK.$anyTimes(); FIELDMOCK.$returns(ROOT); } function tearDownJustifyTests() { tearDownHelper(); } function testJustify() { setUpJustifyTests('def
def
' + 'paragraph
onetag', 1, goog.dom.getElementsByTagName(goog.dom.TagName.P, FIELDMOCK.getElement()) .length); var html = FIELDMOCK.getElement().innerHTML.toLowerCase(); assertNotBadBrElements(html); assertNotContains( 'There should not be empty
foo