/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.gwt.user.client.ui.rta.cmd.internal;
import org.xwiki.gwt.dom.client.Element;
import org.xwiki.gwt.dom.client.Range;
import org.xwiki.gwt.user.client.ui.rta.RichTextAreaTestCase;
import org.xwiki.gwt.user.client.ui.rta.cmd.Executable;
import com.google.gwt.user.client.Command;
/**
* Unit tests for {@link InsertHTMLExecutable}.
*
* @version $Id: a73b8b0df7dc4aa26ad2d1a58266a0781c0d7bb8 $
*/
public class InsertHTMLExecutableTest extends RichTextAreaTestCase
{
/**
* The executable being tested.
*/
private Executable executable;
@Override
protected void gwtSetUp() throws Exception
{
super.gwtSetUp();
if (executable == null) {
executable = new InsertHTMLExecutable(rta);
}
}
/**
* Tests the {@link InsertHTMLExecutable} when the caret goes between DOM child nodes after the selection is
* deleted.
*/
public void testInsertBetweenChildren()
{
deferTest(new Command()
{
public void execute()
{
rta.setHTML("<em>ab</em><strong>cd</strong><ins>ef</ins>");
Range range = rta.getDocument().createRange();
range.setStartBefore(getBody().getChildNodes().getItem(1));
range.setEndAfter(getBody().getChildNodes().getItem(1));
select(range);
assertEquals("cd", rta.getDocument().getSelection().toString());
assertTrue(executable.execute("<!--x-->y<del>z</del>"));
assertEquals("<em>ab</em><!--x-->y<del>z</del><ins>ef</ins>", clean(rta.getHTML()));
}
});
}
/**
* Tests the {@link InsertHTMLExecutable} when the caret goes at the end of an element after the selection is
* deleted.
*/
public void testInsertAfterLastChild()
{
deferTest(new Command()
{
public void execute()
{
rta.setHTML("<em>ab</em><strong>ij</strong>");
Range range = rta.getDocument().createRange();
range.selectNode(getBody().getLastChild());
select(range);
assertEquals("ij", rta.getDocument().getSelection().toString());
assertTrue(executable.execute("#"));
assertEquals("<em>ab</em>#", clean(rta.getHTML()));
}
});
}
/**
* Tests the {@link InsertHTMLExecutable} when the caret goes inside a text node after the selection is deleted.
*/
public void testInsertInTextNode()
{
deferTest(new Command()
{
public void execute()
{
rta.setHTML("xyz");
Range range = rta.getDocument().createRange();
range.setStart(getBody().getFirstChild(), 1);
range.setEnd(getBody().getFirstChild(), 2);
select(range);
assertEquals("y", rta.getDocument().getSelection().toString());
assertTrue(executable.execute("*2<em>=</em>1+"));
assertEquals("x*2<em>=</em>1+z", clean(rta.getHTML()));
}
});
}
/**
* Tests the {@link InsertHTMLExecutable} when the selection spans multiple list items.
*/
public void testReplaceCrossListItemSelection()
{
deferTest(new Command()
{
public void execute()
{
rta.setHTML("<ul><li>foo</li><li>bar</li></ul>");
Range range = rta.getDocument().createRange();
range.setStart(getBody().getFirstChild().getFirstChild().getFirstChild(), 1);
range.setEnd(getBody().getFirstChild().getLastChild().getFirstChild(), 1);
select(range);
assertEquals("oob", rta.getDocument().getSelection().toString());
assertTrue(executable.execute("<img/>"));
assertEquals("<ul><li>f<img>ar</li></ul>", clean(rta.getHTML()));
}
});
}
/**
* Tests if the selection is contracted to perfectly wrap the inserted nodes.
*/
public void testSelectionContractionAfterInsertion()
{
deferTest(new Command()
{
public void execute()
{
doTestSelectionContractionAfterInsertion();
}
});
}
/**
* Tests if the selection is contracted to perfectly wrap the inserted nodes.
*/
private void doTestSelectionContractionAfterInsertion()
{
rta.setHTML("2009");
Range range = rta.getDocument().createRange();
range.setStart(getBody().getFirstChild(), 1);
range.setEnd(getBody().getFirstChild(), 3);
select(range);
assertEquals("00", rta.getDocument().getSelection().toString());
assertTrue(executable.execute("<img title=\"march 11th\"/>"));
assertEquals("2<img title=\"march 11th\">9", clean(rta.getHTML()));
// Lets test if the selection wraps the inserted image.
range = rta.getDocument().getSelection().getRangeAt(0);
assertEquals(getBody(), range.getStartContainer());
assertEquals(getBody(), range.getEndContainer());
assertEquals(1, range.getStartOffset());
assertEquals(2, range.getEndOffset());
}
/**
* Tests if an anchor is properly replaced. What is important is that the caret doesn't move inside a sibling node
* after the selected anchor is deleted. The caret must remain between nodes so that the HTML fragment is inserted
* in the right place.
*/
public void testReplaceAnchor()
{
deferTest(new Command()
{
public void execute()
{
rta.setHTML("<ul><li><a href=\"http://www.xwiki.com\">XWiki</a><sup>tm</sup></li></ul>");
Range range = rta.getDocument().createRange();
range.selectNode(getBody().getFirstChild().getFirstChild().getFirstChild());
select(range);
assertEquals("XWiki", rta.getDocument().getSelection().toString());
assertTrue(executable.execute("x"));
assertEquals("x<sup>tm</sup>", Element.as(getBody().getFirstChild().getFirstChild()).getInnerHTML()
.toLowerCase());
}
});
}
/**
* Tests if the selected text is correctly deleted when it is followed by a space character.
*/
public void testReplaceTextFollowedBySpace()
{
deferTest(new Command()
{
public void execute()
{
rta.setHTML("<p><span>before</span> after</p>");
Range range = rta.getDocument().createRange();
range.selectNodeContents(getBody().getFirstChild().getFirstChild());
select(range);
assertEquals("before", rta.getDocument().getSelection().toString());
assertTrue(executable.execute("start"));
assertEquals("<p><span>start</span> after</p>", getBody().getInnerHTML().toLowerCase());
}
});
}
/**
* Tests if a button is properly replaced. What is important is that the caret doesn't move inside a sibling node
* after the selected button is deleted. The caret must remain between nodes so that the HTML fragment is inserted
* in the right place.
* <p>
* NOTE: We need a special test for replacing a button because buttons support control selection, unlike anchors, so
* they are deleted in another way.
*/
public void testReplaceButton()
{
deferTest(new Command()
{
public void execute()
{
rta.setHTML("<p>before</p><button>albatross</button><p>after</p>");
Range range = rta.getDocument().createRange();
range.selectNode(getBody().getChildNodes().getItem(1));
select(range);
assertEquals("albatross", rta.getDocument().getSelection().toString());
assertTrue(executable.execute("<button>toucan</button>"));
assertEquals("toucan", getBody().getChildNodes().getItem(1).getFirstChild().getNodeValue());
}
});
}
/**
* Selects the first characters of a text node and replaces them.
*/
public void testReplaceTextStart()
{
deferTest(new Command()
{
public void execute()
{
rta.setHTML("<p>123</p><p><em>#</em>456</p>");
Range range = rta.getDocument().createRange();
range.setStart(getBody().getLastChild().getLastChild(), 0);
range.setEnd(getBody().getLastChild().getLastChild(), 2);
select(range);
assertEquals("45", rta.getDocument().getSelection().toString());
assertTrue(executable.execute("87"));
assertEquals("<em>#</em>876", Element.as(getBody().getLastChild()).getInnerHTML().toLowerCase());
}
});
}
/**
* Selects the last characters of a text node and replaces them.
*/
public void testReplaceTextEnd()
{
deferTest(new Command()
{
public void execute()
{
rta.setHTML("<p>123<em>#</em></p><p>456</p>");
Range range = rta.getDocument().createRange();
range.setStart(getBody().getFirstChild().getFirstChild(), 1);
range.setEnd(getBody().getFirstChild().getFirstChild(), 3);
select(range);
assertEquals("23", rta.getDocument().getSelection().toString());
assertTrue(executable.execute("xx"));
assertEquals("1xx<em>#</em>", Element.as(getBody().getFirstChild()).getInnerHTML().toLowerCase());
}
});
}
/**
* Tests if an in-line HTML element is properly replaced when its parent element contains unnormalized text.
*/
public void testReplaceInlineElementWhenUnnormalizedTextIsPresent()
{
deferTest(new Command()
{
public void execute()
{
rta.setHTML("<p>2<em>3</em></p>");
getBody().getFirstChild().insertBefore(rta.getDocument().createTextNode(""),
getBody().getFirstChild().getFirstChild());
getBody().getFirstChild().insertBefore(rta.getDocument().createTextNode("1"),
getBody().getFirstChild().getFirstChild());
Range range = rta.getDocument().createRange();
range.selectNode(getBody().getFirstChild().getLastChild());
select(range);
assertEquals("3", rta.getDocument().getSelection().toString());
assertTrue(executable.execute("4"));
assertEquals("124", Element.as(getBody().getFirstChild()).getInnerHTML().toLowerCase());
}
});
}
/**
* Replaces a selection that starts in a text node which isn't modified after the selected content is deleted. This
* is possible if the part that isn't deleted from the text where the selection ends matches the part that is
* deleted from the text where the selection starts.
*/
public void testReplaceSelectionThatStartsInATextNodeWhichIsntModifiedByTheDelete()
{
deferTest(new Command()
{
public void execute()
{
rta.setHTML("<p>abc<em>de</em>fbc<strong>#</strong></p>");
Range range = rta.getDocument().createRange();
range.setStart(getBody().getFirstChild().getFirstChild(), 1);
range.setEnd(getBody().getFirstChild().getChildNodes().getItem(2), 1);
select(range);
assertEquals("bcdef", rta.getDocument().getSelection().toString());
assertTrue(executable.execute("?"));
assertEquals("a?bc<strong>#</strong>", getBody().getFirstChildElement().getInnerHTML().toLowerCase());
}
});
}
/**
* Replaces a selection that crosses two in-line elements.
*/
public void testReplaceCrossInlineElementSelection()
{
deferTest(new Command()
{
public void execute()
{
// We added an image to the selection to be sure that at least one DOM node is deleted.
rta.setHTML("<p><em>123</em><img/><strong>456</strong></p>");
Range range = rta.getDocument().createRange();
range.setStart(getBody().getFirstChild().getFirstChild().getFirstChild(), 2);
range.setEnd(getBody().getFirstChild().getLastChild().getFirstChild(), 1);
select(range);
assertEquals("34", rta.getDocument().getSelection().toString());
assertTrue(executable.execute("+"));
assertEquals("<em>12+</em><strong>56</strong>", getBody().getFirstChildElement().getInnerHTML()
.toLowerCase());
}
});
}
/**
* Replaces the entire text of a paragraph.
*/
public void testReplaceParagraphText()
{
deferTest(new Command()
{
public void execute()
{
rta.setHTML("<p>alice</p><p>bob</p><p>carol</p>");
Range range = rta.getDocument().createRange();
range.selectNodeContents(getBody().getChildNodes().getItem(1));
select(range);
assertEquals("bob", rta.getDocument().getSelection().toString());
String insertedText = "*";
assertTrue(executable.execute(insertedText));
assertEquals(insertedText, Element.as(getBody().getChildNodes().getItem(1)).getInnerText());
}
});
}
}