Add html file test

This commit is contained in:
Dimitry Ivanov 2018-08-24 23:10:47 +03:00
parent ba30654728
commit 7881420cbd
9 changed files with 244 additions and 10 deletions

View File

@ -119,9 +119,9 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
private boolean isInsidePreTag; private boolean isInsidePreTag;
private Tokeniser tokeniser; // the thing is: we ensure a new line BEFORE block tag
// but not after, so another tag will be placed on the same line (which is wrong)
private CharacterReader reader; private boolean previousIsBlock;
MarkwonHtmlParserImpl( MarkwonHtmlParserImpl(
@ -242,6 +242,8 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
final HtmlTagImpl.InlineImpl inline = new HtmlTagImpl.InlineImpl(name, output.length(), extractAttributes(startTag)); final HtmlTagImpl.InlineImpl inline = new HtmlTagImpl.InlineImpl(name, output.length(), extractAttributes(startTag));
ensureNewLineIfPreviousWasBlock(output);
if (isVoidTag(name) if (isVoidTag(name)
|| startTag.selfClosing) { || startTag.selfClosing) {
@ -305,6 +307,8 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
if (isBlockTag(name)) { if (isBlockTag(name)) {
isInsidePreTag = "pre".equals(name); isInsidePreTag = "pre".equals(name);
ensureNewLine(output); ensureNewLine(output);
} else {
ensureNewLineIfPreviousWasBlock(output);
} }
final int start = output.length(); final int start = output.length();
@ -350,6 +354,11 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
block.closeAt(output.length()); block.closeAt(output.length());
// if it's empty -> we do no care about if it's block or not
if (!block.isEmpty()) {
previousIsBlock = isBlockTag(block.name);
}
if (TAG_PARAGRAPH.equals(name)) { if (TAG_PARAGRAPH.equals(name)) {
appendQuietly(output, '\n'); appendQuietly(output, '\n');
} }
@ -368,6 +377,7 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
if (isInsidePreTag) { if (isInsidePreTag) {
appendQuietly(output, character.getData()); appendQuietly(output, character.getData());
} else { } else {
ensureNewLineIfPreviousWasBlock(output);
trimmingAppender.append(output, character.getData()); trimmingAppender.append(output, character.getData());
} }
} }
@ -410,6 +420,13 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
return blockTag; return blockTag;
} }
protected <T extends Appendable & CharSequence> void ensureNewLineIfPreviousWasBlock(@NonNull T output) {
if (previousIsBlock) {
ensureNewLine(output);
previousIsBlock = false;
}
}
// name here must lower case // name here must lower case
protected static boolean isInlineTag(@NonNull String name) { protected static boolean isInlineTag(@NonNull String name) {
return INLINE_TAGS.contains(name); return INLINE_TAGS.contains(name);

View File

@ -833,6 +833,30 @@ public class MarkwonHtmlParserImplTest {
}); });
} }
@Test
public void newLineAfterBlockTag() {
final MarkwonHtmlParserImpl impl = MarkwonHtmlParserImpl.create();
final StringBuilder output = new StringBuilder();
final String[] fragments = {
"<h1>head #1</h1>just text",
"<h2>head #2</h2><span>in span tag</span>",
"<h3>head #3</h3><custom-tag>in custom-tag</custom-tag>"
};
for (String fragment: fragments) {
impl.processFragment(output, fragment);
}
final String expected = "" +
"head #1\njust text\n" +
"head #2\nin span tag\n" +
"head #3\nin custom-tag";
assertEquals(expected, output.toString());
}
private static class CaptureTagsAction<T> implements MarkwonHtmlParser.FlushAction<T> { private static class CaptureTagsAction<T> implements MarkwonHtmlParser.FlushAction<T> {
boolean called; boolean called;

View File

@ -13,6 +13,7 @@ import ru.noties.markwon.SpannableConfiguration;
import ru.noties.markwon.html.api.MarkwonHtmlParser; import ru.noties.markwon.html.api.MarkwonHtmlParser;
import ru.noties.markwon.renderer.html2.tag.BlockquoteHandler; import ru.noties.markwon.renderer.html2.tag.BlockquoteHandler;
import ru.noties.markwon.renderer.html2.tag.EmphasisHandler; import ru.noties.markwon.renderer.html2.tag.EmphasisHandler;
import ru.noties.markwon.renderer.html2.tag.HeadingHandler;
import ru.noties.markwon.renderer.html2.tag.ImageHandler; import ru.noties.markwon.renderer.html2.tag.ImageHandler;
import ru.noties.markwon.renderer.html2.tag.LinkHandler; import ru.noties.markwon.renderer.html2.tag.LinkHandler;
import ru.noties.markwon.renderer.html2.tag.ListHandler; import ru.noties.markwon.renderer.html2.tag.ListHandler;
@ -69,7 +70,13 @@ public abstract class MarkwonHtmlRenderer {
.handler("ul", listHandler) .handler("ul", listHandler)
.handler("ol", listHandler) .handler("ol", listHandler)
.handler("img", ImageHandler.create()) .handler("img", ImageHandler.create())
.handler("blockquote", new BlockquoteHandler()); .handler("blockquote", new BlockquoteHandler())
.handler("h1", new HeadingHandler(1))
.handler("h2", new HeadingHandler(2))
.handler("h3", new HeadingHandler(3))
.handler("h4", new HeadingHandler(4))
.handler("h5", new HeadingHandler(5))
.handler("h6", new HeadingHandler(6));
} }
@NonNull @NonNull

View File

@ -0,0 +1,22 @@
package ru.noties.markwon.renderer.html2.tag;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import ru.noties.markwon.SpannableConfiguration;
import ru.noties.markwon.html.api.HtmlTag;
public class HeadingHandler extends SimpleTagHandler {
private final int level;
public HeadingHandler(int level) {
this.level = level;
}
@Nullable
@Override
public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) {
return configuration.factory().heading(configuration.theme(), level);
}
}

View File

@ -11,7 +11,7 @@ public class LinkHandler extends SimpleTagHandler {
@Nullable @Nullable
@Override @Override
public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) { public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) {
final String destination = tag.attributes().get("src"); final String destination = tag.attributes().get("href");
if (!TextUtils.isEmpty(destination)) { if (!TextUtils.isEmpty(destination)) {
return configuration.factory().link( return configuration.factory().link(
configuration.theme(), configuration.theme(),

View File

@ -1,15 +1,20 @@
package ru.noties.markwon.renderer.visitor; package ru.noties.markwon.renderer.visitor;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.SpannableStringBuilder; import android.text.SpannableStringBuilder;
import org.commonmark.node.Node; import org.commonmark.node.Node;
import org.junit.ComparisonFailure;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.ParameterizedRobolectricTestRunner; import org.robolectric.ParameterizedRobolectricTestRunner;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator;
import java.util.Map;
import ix.Ix; import ix.Ix;
import ix.IxPredicate; import ix.IxPredicate;
@ -55,13 +60,16 @@ public class SpannableMarkdownVisitorTest {
final SpannableStringBuilder stringBuilder = builder.spannableStringBuilder(); final SpannableStringBuilder stringBuilder = builder.spannableStringBuilder();
System.out.printf("%n%s%n", stringBuilder); System.out.printf("%s: %s%n", file, Arrays.toString(stringBuilder.getSpans(0, stringBuilder.length(), Object.class)));
int index = 0; int index = 0;
for (TestNode testNode : data.output()) { for (TestNode testNode : data.output()) {
index = validate(stringBuilder, index, testNode); index = validate(stringBuilder, index, testNode);
} }
// assert that the whole thing is processed
assertEquals(stringBuilder.length(), index);
} }
private int validate(@NonNull SpannableStringBuilder builder, int index, @NonNull TestNode node) { private int validate(@NonNull SpannableStringBuilder builder, int index, @NonNull TestNode node) {
@ -103,6 +111,8 @@ public class SpannableMarkdownVisitorTest {
final String info = node.toString(); final String info = node.toString();
System.out.printf("%s: %s%n", file, builder.subSequence(index, out));
// we can possibly have parent spans here, should filter them // we can possibly have parent spans here, should filter them
final Object[] spans = builder.getSpans(index, out, Object.class); final Object[] spans = builder.getSpans(index, out, Object.class);
assertTrue(info, spans != null); assertTrue(info, spans != null);
@ -123,14 +133,18 @@ public class SpannableMarkdownVisitorTest {
}) })
.first(null); .first(null);
assertNotNull(info, testSpan); assertNotNull(
format("info: %s, spans: %s", info, Arrays.toString(spans)),
testSpan
);
assertEquals(info, span.name(), testSpan.name()); assertEquals(info, span.name(), testSpan.name());
assertEquals(info, span.attributes(), testSpan.attributes()); assertMapEquals(info, span.attributes(), testSpan.attributes());
return out; return out;
} }
@SuppressWarnings("ConstantConditions")
@NonNull @NonNull
private SpannableConfiguration configuration(@NonNull TestConfig config) { private SpannableConfiguration configuration(@NonNull TestConfig config) {
@ -147,4 +161,49 @@ public class SpannableMarkdownVisitorTest {
.factory(factory) .factory(factory)
.build(); .build();
} }
private static void assertMapEquals(
@NonNull String message,
@NonNull Map<String, String> expected,
@NonNull Map<String, String> actual) {
boolean result = true;
if (expected.size() == actual.size()) {
for (Map.Entry<String, String> entry : expected.entrySet()) {
if (!actual.containsKey(entry.getKey())
|| !equals(entry.getValue(), actual.get(entry.getKey()))) {
result = false;
break;
}
}
}
if (!result) {
final Comparator<Map.Entry<String, String>> comparator = new Comparator<Map.Entry<String, String>>() {
@Override
public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
return o1.getKey().compareTo(o2.getKey());
}
};
final String e = Ix.from(expected.entrySet())
.orderBy(comparator)
.toList()
.toString();
final String a = Ix.from(actual.entrySet())
.orderBy(comparator)
.toList()
.toString();
throw new ComparisonFailure(message, e, a);
}
}
private static boolean equals(@Nullable Object o1, @Nullable Object o2) {
return o1 != null
? o1.equals(o2)
: o2 == null;
}
@NonNull
private static String format(@NonNull String message, Object... args) {
return String.format(message, args);
}
} }

View File

@ -186,7 +186,7 @@ class TestFactory implements SpannableFactory {
final int length = pairs.length; final int length = pairs.length;
final Map<String, String> map = new HashMap<>(length); final Map<String, String> map = new HashMap<>(length);
for (Pair pair : pairs) { for (Pair pair : pairs) {
map.put(pair.key, String.valueOf(pair.value)); map.put(pair.key, pair.value == null ? null : String.valueOf(pair.value));
} }
return map; return map;
} }

View File

@ -0,0 +1,105 @@
input: |-
<h1>html</h1>
<h2>emphasis</h2>
<i>i</i><em>em</em><cite>cite</cite><dfn>dfn</dfn>
<h2>strong-emphasis</h2>
<b>b</b><strong>strong</strong>
<h2>super-script</h2>
<sup>sup</sup>
<h2>sub-script</h2>
<sub>sub</sub>
<h2>underline</h2>
<u>u</u><ins>ins</ins>
<h2>strike</h2>
<s>s</s><del>del</del>
<h2>link</h2>
<a href="a://href">a</a>
<h2>unordered-list</h2>
<ul><li>ul1<li>ul2</ul>
<h2>ordered-list</h2>
<ol><li>ol1<li>ol2</ol>
<h2>image</h2>
<img src="img://src" alt="img">
<h2>blockquote</h2>
<blockquote>blockquote</blockquote>
<h3>3</h3>
<h4>4</h4>
<h5>5</h5>
<h6>6</h6>
config:
use-html: true
output:
- h1: "html"
- text: "\n"
- h2: "emphasis"
- text: "\n"
- i: "i"
- i: "em"
- i: "cite"
- i: "dfn"
- text: "\n"
- h2: "strong-emphasis"
- text: "\n"
- b: "b"
- b: "strong"
- text: "\n"
- h2: "super-script"
- text: "\n"
- sup: "sup"
- text: "\n"
- h2: "sub-script"
- text: "\n"
- sub: "sub"
- text: "\n"
- h2: "underline"
- text: "\n"
- u: "u"
- u: "ins"
- text: "\n"
- h2: "strike"
- text: "\n"
- s: "s"
- s: "del"
- text: "\n"
- h2: "link"
- text: "\n"
- a: "a"
attrs:
href: "a://href"
- text: "\n"
- h2: "unordered-list"
- text: "\n"
- ul: "ul1"
- text: "\n"
- ul: "ul2"
- text: "\n"
- h2: "ordered-list"
- text: "\n"
- ol: "ol1"
attrs:
start: 1
- text: "\n"
- ol: "ol2"
attrs:
start: 2
- text: "\n"
- h2: "image"
- text: "\n"
- img: "img"
attrs:
src: "img://src"
- text: "\n"
- h2: "blockquote"
- text: "\n"
- blockquote: "blockquote"
- text: "\n"
- h3: "3"
- text: "\n"
- h4: "4"
- text: "\n"
- h5: "5"
- text: "\n"
- h6: "6"

View File

@ -3,6 +3,6 @@ input: "![image](#href)"
output: output:
- img: "image" - img: "image"
attrs: attrs:
scr: "#href" src: "#href"
imageSize: null imageSize: null
replacementTextIsLink: false replacementTextIsLink: false