diff options
Diffstat (limited to 'libs/editor/example/src/test/java/org/wordpress/android/editor/HtmlStyleTextWatcherTest.java')
-rw-r--r-- | libs/editor/example/src/test/java/org/wordpress/android/editor/HtmlStyleTextWatcherTest.java | 496 |
1 files changed, 496 insertions, 0 deletions
diff --git a/libs/editor/example/src/test/java/org/wordpress/android/editor/HtmlStyleTextWatcherTest.java b/libs/editor/example/src/test/java/org/wordpress/android/editor/HtmlStyleTextWatcherTest.java new file mode 100644 index 000000000..beaabe31c --- /dev/null +++ b/libs/editor/example/src/test/java/org/wordpress/android/editor/HtmlStyleTextWatcherTest.java @@ -0,0 +1,496 @@ +package org.wordpress.android.editor; + +import android.text.Editable; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.style.ForegroundColorSpan; +import android.text.style.RelativeSizeSpan; +import android.text.style.StyleSpan; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import static org.junit.Assert.assertEquals; + +@Config(sdk = 18) +@RunWith(RobolectricTestRunner.class) +public class HtmlStyleTextWatcherTest { + + private HtmlStyleTextWatcherForTests mWatcher; + private Editable mContent; + private boolean mUpdateSpansWasCalled; + private HtmlStyleTextWatcher.SpanRange mSpanRange; + + @Before + public void setUp() { + mWatcher = new HtmlStyleTextWatcherForTests(); + mUpdateSpansWasCalled = false; + } + + @Test + public void testTypingNormalText() { + // -- Test typing in normal text (non-HTML) in an empty document + mContent = new SpannableStringBuilder("a"); + + mWatcher.onTextChanged(mContent, 0, 0, 1); // Typed "a" + mWatcher.afterTextChanged(mContent); + + assertEquals(false, mUpdateSpansWasCalled); + + mContent = new SpannableStringBuilder("ab"); + + mWatcher.onTextChanged(mContent, 1, 0, 1); // Typed "b" + mWatcher.afterTextChanged(mContent); + + assertEquals(false, mUpdateSpansWasCalled); + + + // -- Test typing in normal text after exiting tags + mContent = new SpannableStringBuilder("text <b>bold</b> a"); + + mWatcher.onTextChanged(mContent, 17, 0, 1); // Typed "a" + mWatcher.afterTextChanged(mContent); + + assertEquals(false, mUpdateSpansWasCalled); + + + // -- Test typing in normal text before exiting tags + mContent = new SpannableStringBuilder("text a <b>bold</b>"); + + mWatcher.onTextChanged(mContent, 5, 0, 1); // Typed "a" + mWatcher.afterTextChanged(mContent); + + assertEquals(false, mUpdateSpansWasCalled); + } + + @Test + public void testTypingInOpeningTag() { + // Test with several different cases of pre-existing text + String[] previousTextCases = new String[]{"", "plain text", "<i>", + "<blockquote>some existing content</blockquote> "}; + for (String initialText : previousTextCases) { + int offset = initialText.length(); + mUpdateSpansWasCalled = false; + + // -- Test typing in an opening tag symbol + mContent = new SpannableStringBuilder(initialText + "<"); + + mWatcher.onTextChanged(mContent, offset, 0, 1); + mWatcher.afterTextChanged(mContent); + + // No formatting should be applied/removed + assertEquals(false, mUpdateSpansWasCalled); + + + // -- Test typing in the tag name + mContent = new SpannableStringBuilder(initialText + "<b"); + + mWatcher.onTextChanged(mContent, offset + 1, 0, 1); + mWatcher.afterTextChanged(mContent); + + // No formatting should be applied/removed + assertEquals(false, mUpdateSpansWasCalled); + + + // -- Test typing in a closing tag symbol + mContent = new SpannableStringBuilder(initialText + "<b>"); + + mWatcher.onTextChanged(mContent, offset + 2, 0, 1); + mWatcher.afterTextChanged(mContent); + + assertEquals(offset, mSpanRange.getOpeningTagLoc()); + assertEquals(offset + 3, mSpanRange.getClosingTagLoc()); + } + } + + @Test + public void testTypingInClosingTag() { + // Test with several different cases of pre-existing text + String[] previousTextCases = new String[]{"<b>stuff", "plain text <b>stuff", "<i><b>stuff", + "<blockquote>some existing content</blockquote> <b>stuff"}; + + for (String initialText : previousTextCases) { + int offset = initialText.length(); + mUpdateSpansWasCalled = false; + + // -- Test typing in an opening tag symbol + mContent = new SpannableStringBuilder(initialText + "<"); + + mWatcher.onTextChanged(mContent, offset, 0, 1); + mWatcher.afterTextChanged(mContent); + + // No formatting should be applied/removed + assertEquals(false, mUpdateSpansWasCalled); + + + // -- Test typing in the closing tag slash + mContent = new SpannableStringBuilder(initialText + "</"); + + mWatcher.onTextChanged(mContent, offset + 1, 0, 1); + mWatcher.afterTextChanged(mContent); + + // No formatting should be applied/removed + assertEquals(false, mUpdateSpansWasCalled); + + // -- Test typing in the tag name + mContent = new SpannableStringBuilder(initialText + "</b"); + + mWatcher.onTextChanged(mContent, offset + 2, 0, 1); + mWatcher.afterTextChanged(mContent); + + // No formatting should be applied/removed + assertEquals(false, mUpdateSpansWasCalled); + + + // -- Test typing in a closing tag symbol + mContent = new SpannableStringBuilder(initialText + "</b>"); + + mWatcher.onTextChanged(mContent, offset + 3, 0, 1); + mWatcher.afterTextChanged(mContent); + + assertEquals(offset, mSpanRange.getOpeningTagLoc()); + assertEquals(offset + 4, mSpanRange.getClosingTagLoc()); + } + } + + @Test + public void testTypingInTagWithSurroundingTags() { + // Spans in this case will be applied until the end of the next tag + // This fixes a pasting bug and might be refined later + // -- Test typing in the opening tag symbol + mContent = new SpannableStringBuilder("some <del>text</del> < <b>bold text</b>"); + + mWatcher.onTextChanged(mContent, 21, 0, 1); // Added lone "<" + mWatcher.afterTextChanged(mContent); + + assertEquals(21, mSpanRange.getOpeningTagLoc()); + assertEquals(26, mSpanRange.getClosingTagLoc()); + + + // -- Test typing in the tag name + mContent = new SpannableStringBuilder("some <del>text</del> <i <b>bold text</b>"); + + mWatcher.onTextChanged(mContent, 22, 0, 1); + mWatcher.afterTextChanged(mContent); + + assertEquals(21, mSpanRange.getOpeningTagLoc()); + assertEquals(27, mSpanRange.getClosingTagLoc()); + + + // -- Test typing in the closing tag symbol + mContent = new SpannableStringBuilder("some <del>text</del> <i> <b>bold text</b>"); + + mWatcher.onTextChanged(mContent, 23, 0, 1); + mWatcher.afterTextChanged(mContent); + + assertEquals(21, mSpanRange.getOpeningTagLoc()); + assertEquals(28, mSpanRange.getClosingTagLoc()); + } + + @Test + public void testTypingInLoneClosingSymbol() { + // -- Test typing in an isolated closing tag symbol + mContent = new SpannableStringBuilder("some text >"); + + mWatcher.onTextChanged(mContent, 10, 0, 1); + mWatcher.afterTextChanged(mContent); + + // No formatting should be applied/removed + assertEquals(false, mUpdateSpansWasCalled); + + + // -- Test typing in an isolated closing tag symbol with surrounding tags + mContent = new SpannableStringBuilder("some <b>tex>t</b>"); + + mWatcher.onTextChanged(mContent, 11, 0, 1); // Added lone ">" + mWatcher.afterTextChanged(mContent); + + // The span in this case will be applied from the start of the previous tag to the end of the next tag + assertEquals(5, mSpanRange.getOpeningTagLoc()); + assertEquals(17, mSpanRange.getClosingTagLoc()); + } + + @Test + public void testTypingInEntity() { + // Test with several different cases of pre-existing text + String[] previousTextCases = new String[]{"", "plain text", "ρ", + "<blockquote>some existing content †</blockquote> "}; + for (String initialText : previousTextCases) { + int offset = initialText.length(); + mUpdateSpansWasCalled = false; + + // -- Test typing in the entity's opening '&' + mContent = new SpannableStringBuilder(initialText + "&"); + + mWatcher.onTextChanged(mContent, offset, 0, 1); + mWatcher.afterTextChanged(mContent); + + // No formatting should be applied/removed + assertEquals(false, mUpdateSpansWasCalled); + + + // -- Test typing in the entity's main text + mContent = new SpannableStringBuilder(initialText + "&"); + + mWatcher.onTextChanged(mContent, offset + 3, 0, 1); + mWatcher.afterTextChanged(mContent); + + // No formatting should be applied/removed + assertEquals(false, mUpdateSpansWasCalled); + + + // -- Test typing in the entity's closing ';' + mContent = new SpannableStringBuilder(initialText + "&"); + + mWatcher.onTextChanged(mContent, offset + 4, 0, 1); + mWatcher.afterTextChanged(mContent); + + assertEquals(offset, mSpanRange.getOpeningTagLoc()); + assertEquals(offset + 5, mSpanRange.getClosingTagLoc()); + } + } + + @Test + public void testAddingTagFromFormatBar() { + // -- Test adding a tag to an empty document + mContent = new SpannableStringBuilder("<b>"); + + mWatcher.onTextChanged(mContent, 0, 0, 3); + mWatcher.afterTextChanged(mContent); + + assertEquals(0, mSpanRange.getOpeningTagLoc()); + assertEquals(3, mSpanRange.getClosingTagLoc()); + + + // -- Test adding a tag at the end of a document with text + mContent = new SpannableStringBuilder("stuff<b>"); + + mWatcher.onTextChanged(mContent, 5, 0, 3); + mWatcher.afterTextChanged(mContent); + + assertEquals(5, mSpanRange.getOpeningTagLoc()); + assertEquals(8, mSpanRange.getClosingTagLoc()); + + + // -- Test adding a tag at the end of a document containing other html + mContent = new SpannableStringBuilder("some text <i>italics</i> <b>"); + + mWatcher.onTextChanged(mContent, 25, 0, 3); // Added "<b>" + mWatcher.afterTextChanged(mContent); + + assertEquals(25, mSpanRange.getOpeningTagLoc()); + assertEquals(28, mSpanRange.getClosingTagLoc()); + + + // -- Test adding a tag at the start of a document with text + mContent = new SpannableStringBuilder("<b>some text"); + + mWatcher.onTextChanged(mContent, 0, 0, 3); + mWatcher.afterTextChanged(mContent); + + assertEquals(0, mSpanRange.getOpeningTagLoc()); + assertEquals(3, mSpanRange.getClosingTagLoc()); + + + // -- Test adding a tag at the start of a document containing other html + mContent = new SpannableStringBuilder("<b>some text <i>italics</i>"); + + mWatcher.onTextChanged(mContent, 0, 0, 3); + mWatcher.afterTextChanged(mContent); + + assertEquals(0, mSpanRange.getOpeningTagLoc()); + assertEquals(3, mSpanRange.getClosingTagLoc()); + + + // -- Test adding a tag within another tag pair + mContent = new SpannableStringBuilder("<b>some <i>text</b>"); + + mWatcher.onTextChanged(mContent, 8, 0, 3); // Added <i> + mWatcher.afterTextChanged(mContent); + + assertEquals(8, mSpanRange.getOpeningTagLoc()); + assertEquals(11, mSpanRange.getClosingTagLoc()); + + + // -- Test adding a closing tag within another tag pair + mContent = new SpannableStringBuilder("<b>some <i>text</i></b>"); + + mWatcher.onTextChanged(mContent, 15, 0, 4); // Added "</i>" + mWatcher.afterTextChanged(mContent); + + assertEquals(15, mSpanRange.getOpeningTagLoc()); + assertEquals(19, mSpanRange.getClosingTagLoc()); + } + + @Test + public void testAddingListTagsFromFormatBar() { + // -- Test adding a list tag to an empty document + mContent = new SpannableStringBuilder("<ul>\n\t<li>"); + + mWatcher.onTextChanged(mContent, 0, 0, 10); + mWatcher.afterTextChanged(mContent); + + assertEquals(0, mSpanRange.getOpeningTagLoc()); + assertEquals(10, mSpanRange.getClosingTagLoc()); + + + // -- Test adding a closing list tag + mContent = new SpannableStringBuilder("<ul>\n" + //5 + "\t<li>list item</li>\n" + //20 + "\t<li>another list item</li>\n" + //22 + "</ul>"); + + mWatcher.onTextChanged(mContent, 47, 0, 11); // Added "</li>\n</ul>" + mWatcher.afterTextChanged(mContent); + + assertEquals(47, mSpanRange.getOpeningTagLoc()); + assertEquals(58, mSpanRange.getClosingTagLoc()); + } + + @Test + public void testDeletingPartsOfTag() { + // -- Test deleting different characters within a tag + mContent = new SpannableStringBuilder("<b>stuff</b>"); + + int deletedChar = 0; + mWatcher.beforeTextChanged(mContent, deletedChar, 1, 0); + // Deleted characters are removed from the string between beforeTextChanged() and onTextChanged() + mContent.delete(deletedChar, deletedChar + 1); + mWatcher.onTextChanged(mContent, deletedChar, 1, 0); + mWatcher.afterTextChanged(mContent); + + // "b>" should be re-styled + assertEquals(0, mSpanRange.getOpeningTagLoc()); + assertEquals(2, mSpanRange.getClosingTagLoc()); + + for (int i = 8; i < 12; i++) { + mContent = new SpannableStringBuilder("<b>stuff</b>"); + + mWatcher.beforeTextChanged(mContent, i, 1, 0); + mContent.delete(i, i + 1); + mWatcher.afterTextChanged(mContent); + + // Style should be updated starting from the end of 'stuff' + assertEquals(8, mSpanRange.getOpeningTagLoc()); + assertEquals(mContent.length(), mSpanRange.getClosingTagLoc()); + } + } + + @Test + public void testPasteTagPair() { + // -- Test pasting in a set of opening and closing tags at the end of the document + mContent = new SpannableStringBuilder("text <b></b>"); + + mWatcher.onTextChanged(mContent, 5, 0, 7); + mWatcher.afterTextChanged(mContent); + + assertEquals(5, mSpanRange.getOpeningTagLoc()); + assertEquals(12, mSpanRange.getClosingTagLoc()); + } + + @Test + public void testCutAndPasteTagPart() { + // -- Test cutting a tag and part of another tag from the document + mContent = new SpannableStringBuilder("test <b></b> <i>italics</i>"); + + mWatcher.beforeTextChanged(mContent, 5, 4, 0); // Deleted "<b><" + mContent.delete(5, 9); + mWatcher.onTextChanged(mContent, 5, 4, 0); + mWatcher.afterTextChanged(mContent); + + assertEquals(5, mSpanRange.getOpeningTagLoc()); + assertEquals(8, mSpanRange.getClosingTagLoc()); + + + // -- Test pasting the cut text back in + mContent = new SpannableStringBuilder("test <b></b> <i>italics</i>"); + mWatcher.onTextChanged(mContent, 5, 0, 4); // Pasted "<b><" back in + mWatcher.afterTextChanged(mContent); + + assertEquals(5, mSpanRange.getOpeningTagLoc()); + assertEquals(12, mSpanRange.getClosingTagLoc()); + } + + @Test + public void testCutAndPasteTagPartReplacingText() { + // -- Test pasting cut text while text is selected + // Pasted "<b><", replacing "st " of "test " + mContent = new SpannableStringBuilder("test /b> <i>italics</i>"); + mWatcher.beforeTextChanged(mContent, 2, 3, 4); + mContent = new SpannableStringBuilder("te<b></b> <i>italics</i>"); + mWatcher.onTextChanged(mContent, 2, 3, 4); + mWatcher.afterTextChanged(mContent); + + // Should re-style whole document + assertEquals(0, mSpanRange.getOpeningTagLoc()); + assertEquals(mContent.length(), mSpanRange.getClosingTagLoc()); + + + // -- Test pasting cut text while text is selected, case 2 + // Pasted "i>", replacing "test " + mContent = new SpannableStringBuilder("<test italics</i>"); + mWatcher.beforeTextChanged(mContent, 1, 5, 2); + mContent = new SpannableStringBuilder("<i>italics</i>"); + mWatcher.onTextChanged(mContent, 1, 5, 2); + mWatcher.afterTextChanged(mContent); + + // Should re-style whole document + assertEquals(0, mSpanRange.getOpeningTagLoc()); + assertEquals(mContent.length(), mSpanRange.getClosingTagLoc()); + } + + @Test + public void testNoChange() { + + mWatcher.beforeTextChanged("sample", 0, 0, 0); + mWatcher.onTextChanged("sample", 0, 0, 0); + mWatcher.afterTextChanged(null); + + // No formatting should be applied/removed + assertEquals(false, mUpdateSpansWasCalled); + } + + @Test + public void testUpdateSpans() { + // -- Test tag styling + HtmlStyleTextWatcher watcher = new HtmlStyleTextWatcher(); + Spannable content = new SpannableStringBuilder("<b>stuff</b>"); + watcher.updateSpans(content, new HtmlStyleTextWatcher.SpanRange(0, 3)); + + assertEquals(1, content.getSpans(0, 3, ForegroundColorSpan.class).length); + + // -- Test entity styling + content = new SpannableStringBuilder("text & more text"); + watcher.updateSpans(content, new HtmlStyleTextWatcher.SpanRange(5, 10)); + + assertEquals(1, content.getSpans(5, 10, ForegroundColorSpan.class).length); + assertEquals(1, content.getSpans(5, 10, StyleSpan.class).length); + assertEquals(1, content.getSpans(5, 10, RelativeSizeSpan.class).length); + + // -- Test comment styling + content = new SpannableStringBuilder("text <!--comment--> more text"); + watcher.updateSpans(content, new HtmlStyleTextWatcher.SpanRange(5, 19)); + + assertEquals(1, content.getSpans(5, 19, ForegroundColorSpan.class).length); + assertEquals(1, content.getSpans(5, 19, StyleSpan.class).length); + assertEquals(1, content.getSpans(5, 19, RelativeSizeSpan.class).length); + + content = new SpannableStringBuilder("<b>stuff</b>"); + watcher.updateSpans(content, new HtmlStyleTextWatcher.SpanRange(0, 3)); + + watcher.updateSpans(content, new HtmlStyleTextWatcher.SpanRange(0, 42)); + assertEquals(1, content.getSpans(0, 3, ForegroundColorSpan.class).length); + + } + + private class HtmlStyleTextWatcherForTests extends HtmlStyleTextWatcher { + @Override + protected void updateSpans(Spannable s, SpanRange spanRange) { + mSpanRange = spanRange; + mUpdateSpansWasCalled = true; + } + } +} |